Safari + JXA その 2

この間 の続きです。

いや、書いていてですね、ちょっとかったるかったんですよ、doJavaScript。

理由はいくつかあって、要するに文字列にしないといけないので、syntax highlighting が使えないとか。
それ以前の問題として、JavaScript って here document ってないですよね、多分。
複数行にまたがるような長いものは書きづらいと。

なもんで、ゴリゴリやってもいいんだけど、あまりに汚らしいので、ちょっとは綺麗に書こうかしらと。

今回の参考記事。

Marionette = function() {
    this.app = Application('Safari');
    this.world = this.app.windows[0].tabs[0];
};

Marionette.prototype.doJavaScript = function(script) {
    return this.app.doJavaScript('(' + script.toString() + ')()', {in: this.world});
}

Marionette.prototype.say = function(message) {
    var app = Application.currentApplication();
    app.includeStandardAdditions = true;
    app.say(message);
}

m = new Marionette();

result = m.doJavaScript(function(){
    return confirm("Hello, world!");
});

/* NG
var hello = "Hello, world!"
result = m.doJavaScript(function(){
    return confirm(hello);
});
*/

m.say((result ? 'OK' : 'Cancel') + ' returned');

というわけで、半分くらいうまくいったようですが、変数の受け渡しは、当然に NG でした。

これは、JSON.stringify() を使えばなんとかなるんじゃないかと思います。

(function (argv) {
    argv = JSON.parse(argv);
    console.log(argv);
    return 0;
})(JSON.stringify(['foo', 'bar', 'baz']));

say についても、Application.currentApplication() に対して呼べばうまく行きました。
ただ、日本語を喋れません。

Terminal の say コマンドだと -v で Kyoko を指定すればいいんですが、まあ、別に無理して日本語を喋らせる必要はないので、英語のままでいい気もしています。

そういえば、JXA で doShellScript すればいいという話かもしれません。

Safari + JXA

STORES やなんかもそうなんですが、意外とかゆいところに手が届きません。

Yahoo! Auctions なども、API で出品できるようにするといっていたのですが、問い合わせたら、できなくなりました、ということなので、ブラウザの側をスクリプトで制御して、決まった動作を(半)自動化しているのですが、今までは AppleScript で行っていたので、AppleScript と JavaScript、更に shell とのちゃんぽんだったわけです。

別に動いているものを特に書き換えるつもりもないのですが、最近覚えるよりも忘れるほうが早いので、なるべく単一の言語でやりたい、というのがありまして、重い腰を上げて JXA なるものを初めてみました。

というわけで、かなりの人にどうでもいいネタだと思いますが、備忘録として。

Imperia Online という村ゲーをしているのですが、これの特定の村を表示するスクリプトを書いてみました。
いや、ブックマークが 20 件までしか登録できないので、不便なので。

function run(argv) {
    se = Application('System Events');
    if (se.uiElementsEnabled()) {
        app = Application('Safari');
        app.includeStandardAdditions = true;
        
        //app.activate();
        //delay(1); // waiting for competition of ativatation
        
        // search Imperia Online Document
        world = null;
        for (i = 0; ! world && i < app.windows().length; ++i) {
            for (j = 0; j < app.windows[i].tabs().length; ++j) {
                console.log(app.windows[i].tabs[j]);
                if (app.windows[i].tabs[j].url().match(/imperiaonline/)) {
                    world = app.windows[i].tabs[j];
                    break;
                }
            }
        }
        
        if (!world) {
            return;
        }
        
        try {
            app.doJavaScript("xajax_switchVillageToGlobalMap('global-map-window', {x: 1701, y: -614})", {in:world});
        }
        catch (e) {
            console.log(e);
        }
    }
    else {
        with (Application('System Preferences')) {
            pane = panes.byId('com.apple.preference.security');
            pane.anchors['Privacy_Accessibility'].reveal();
            activate();
        }
    }
}

参考にしたのは、この辺り。

動作的には、Safari のすべてのウインドウを取得して、ウインドウの中にぶら下がっているすべてのタブを巡回し、URL をマッチして目的のタブを調べる、という部分、あとは、そこで取得したタブに対して、JavaScript を実行する、という部分で成り立っています。

例えば、Yahoo! Auctions で発送作業をしようと思った時など、発送情報が開かれているタブと、クリックポストの発送入力画面が開かれているタブと、その両方を取得して、それぞれで doJavaScript をすれば、通常できないクロスサイト・スクリプティングが簡単に可能になり、スクリプトメニューにそういうよく使うスクリプトは登録しておくこともでき、また、所詮はスクリプトですから、サイト側のデザイン変更などにも容易、かつ、迅速に対応ができるわけです。

ところで、今のところのハマっている部分。

いちいちエラー画面を出すのがイヤなので、通常は、say を使って喋らせているんですが、うまくいかないんですよね。
多分、Accessibility の権限設定が間違えているんだと思うんだけど。

こちらで参考にしたのは、openspc。
一体何年お世話になりっぱなしなんだろう。