ラベル JavaScript の投稿を表示しています。 すべての投稿を表示
ラベル JavaScript の投稿を表示しています。 すべての投稿を表示

2009年11月20日金曜日

Tombloo スクリプト

最近Yahoo PipesとTwitter Feedの連携がうまくいかずブクマしたものがなかなか自動投稿されない悲しい状態になってたので、Tomblooから直接ついったーへ投稿する方式に切り替えようと思う。
ただ、Tomblooのデフォルト投稿形式が気に入らないので何とかカスタマイズしたいと思ったんだけど、scriptフォルダに何か.jsファイルを置けばいろいろできるという記述までは見つけたが、その先の情報がぜんぜんみつからん。

参考資料

公式ページっぽいところもいまいちよくわからんし
結局この辺をみながら見よう見まねでコードを書いてみた。

大まかな動作

どうもaddBeforeという関数とaddAroundという関数があって、こいつらを書いてやるとアイテムを加工できるみたい。書式としては
addAround(Twitter, 'post', function(proceed, args) {
    return proceed(args);
});
addBefore(Twitter, 'post', function(ps) {
});
この2種類のようで、Twitterのところがそれぞれのサービス名にあたるようだ。あくまで正式なマニュアルを見つけられなかったので勘でしかないんだが・・・
んでaddAroundのproceedってのが関数オブジェクト?みたいのでargsを渡してやると実際に投稿されるみたい。戻り値はエラーとかリザルトとかの結果オブジェクトみたいなのでreturnで返してやればいいようだ。
addBeforeはそれ以前の段階でのアイテムの加工のようだ。
addBeforeのpsオブジェクトとaddAroundのargs[0]は同じものみたい
メンバーに
page
pageUrl
type
item
itemUrl
tags
description
これらを含んでるようだ。ここを加工してやればいいみたい。

アイテムオブジェクトは参照?

addBeforeとかaddAroundに渡ってるくる変数がどうも参照みたいで、途中で変に加工すると別のところにクロスポストされるときに加工済みのものがわたってしまってtwitterのだけ加工したいとかってのができない。さっきのリンクページにあったおまじない
var ps = args[0] = update({}, args[0]);
をやってpsを加工してやってから proceed(args)してやると
そのサービスに対してのみ加工したものが反映されるみたい。意味はしらん!俺はJavaScript使いじゃねー

最終目標

というわけで、目的はTwitterに投稿するときに「ページタイトル:コメント 短縮URL」って投稿されるものを「[B!]コメント:ページタイトル 短縮URL」にすること
で、出来上がったスクリプトがこれ
addAround(Twitter, 'post', function(proceed, args) {
    switch (args[0].type) {
        case "link":
            // クロスポストを考慮し、psをそのまま改変しない
            var ps = args[0] = update({}, args[0]);
            ps.item = ps.item || "";
            ps.item = "[B!]" + ps.description + ":" + ps.item;
            delete ps.description;
            break;
    }
    return proceed(args);
});
とりあえずlinkの時だけ対応してみた。今日はとりあえずここまでー

2009年6月25日木曜日

今日はまったJavaScript

使い慣れてない言語はだめだね、今日はまったバグはJavaScript
数値の配列を GM_setValue("hoge", data.join(",")); 見たいにして保存しておいて、data = GM_getValue("hoge","").split(","); で取り出して使ってたんだけどそこでvar hoge = data[1] + 1; とかして使ってたんだけど、これdata[1] = 1 だと hoge = "11" になるのねorz
数値を少しずつ増やすようなコードのつもりで1桁ずつ増えてた・・・型の無い言語はこういうことがおきるのか、なんか防ぐ方法あるん?気をつけるしかないのか?

2009年6月23日火曜日

JavaScriptメモリ不足

最近書いたGreasemonkeyのスクリプトがメモリ不足を示すエラーで動かなくなってしまった。
エラー内容は以下のとおり

エラー: Component returned failure code: 0x8007000e (NS_ERROR_OUT_OF_MEMORY) [nsIXPCComponents_Utils.evalInSandbox]

そんなにたいしたことしていないコードなのになんでだろうって思って調べたら、どうもArrayのsort関数で死亡してるらしい。そこで原因判明
この配列ID値を連想配列のように使っていて if(id in array) みたいな書き方をしてたんだけど、このidが別に1から振られてる感じじゃなかったので array[2034857103] = "hoge"; みたいな使われ方をしててどうも0~2034857102までの配列が内部的に生成されてしまってたみたい。
それをソートしようとするものだから膨大なメモリが使われちゃったのかな?
idを["id"] = id; として連想配列にして格納するようにしたらすんなり動きました。使い慣れてない言語はこういうトラップにはまる

2009年5月23日土曜日

Greasemonkey(JavaScript)入門

ここ2週間ほどGreasemonkeyのスクリプトを毎日書き続けてる。
まじめにJavaScriptを書いたのが今回が初めてなので色々作法がわからずつまずいたところとかがあったのでまとめておく。解決した項目も未解決の項目も・・・長文になりそうだ

最初にJavaScriptの第一印象なんだけど、なんというか自由な言語。好き勝手できるし、しちゃうと収拾が付かなくなる。最初に作られた仕様の拡張拡張でここまできちゃってもうぐちゃぐちゃなのかな~という感じ
こんな言語で高度で複雑なものなんか作れるわけ無いと最初は思ってたけど色々使ってるうちに意外といける気もしてたり・・・コーディングに自分なりの制約をかけてやればちゃんと使えるのかな?

まず、Greasemonkeyを使うにあたっての開発環境だけど、ブラウザはFireFox3 アドオンにもちろんGreasemonkey、あとFireBug、これが無いと色々不便。最低限必要なものはこんな感じか
で、肝心のエディタなんだけど・・・これ普通何つかうものなの?
最初はnotepad.exeでちくちく書いてたんだけど8タブなのがどうしても我慢できずサクラエディタに切り替え。最近のプログラマにありがちなオートコンプリートが無いとコードがまともに書けない状態に陥りVisual Web Developer 2008 Express Edition をインストールして単純にエディタとして使ってるのが現状。普段使ってるエディタがVisualStudioなのでやっぱり使い慣れたエディタが一番だよね

デバッグについて、GM_logって関数でいわゆるprintfデバッグのようなことができる・・・が、ブレークポイントとか張れないのか!?FireBugでサーバー側のスクリプトに対してはブレークポイントが張れたんだけどgreasemonkeyのスクリプトが一覧に出てこないのでブレークが張れずいまだにGM_logでがんばってデバッグしてる・・・ちょっと調べたけどいまいちやり方がわからんかった。

配列をforeachしたい

for(var i in list)
{
    list[i];
}
とりあえずコレでいいみたい。ただプロトタイプ拡張(?)をすると追加したメンバー関数もリストアップされちゃうので注意らしい。まだその域に達していないので良くわからない。単純な配列であれば問題ないみたい。

ページ遷移のたびに変数が消えてしまう・・・
GM_setValue GM_getValueってのを使ってページ遷移を通じで変数の受け渡しをするみたい。だけど連想配列とか丸ごとsetValue getValueしたいけど対応している型がbool int string の3種類だけみたい。
ちょっと調べてみたらFireFox3.1からはJSON形式なるものになってオブジェクトがまるっと保存できるそうな?まぁJSONってのが何なのかわかってないんだけどねw 現状では無理みたい。toSource関数とevalってのを組み合わせることでJSON形式の文字列としてうんたらってのもあったけどよくわからなかったのでとりあえず保留。1次連想配列を id:name=value:name=value,id:name=value:name=value っていう独自の形式で出力、入力する関数を作って対応してしまった。もうちょいすればきっとこの辺を簡単にやってくれるようになるはず!

ページを開いて5秒してから実行したい
setTimeoutなる関数を使う。
setTimeout(function(){
    //code...
}, 5 * 1000);
比較的簡単でこりゃ便利と思ってたら思わぬ罠が・・・どうもこのfunction(){}の中が実行されるのが指定した時間の後になるため、function内部でfunction外部の変数を使ってると変数が変わってしまうと動作時に参照される変数も変わってしまう。ややこしいなつまり
var list = ["hoge", "boe", "moe"];
for(var i in list){
    setTimeout(function(){
        alert(list[i]);
    }, 5 * 1000);
}
こんなコードを実行するとhoge boe moe と表示してほしいのに
moe moe moe と表示されてしまう。ようはfor文が回りきった後のi=2 の状態に対してfunctionが呼ばれてしまうからみたい。回避策としては匿名の関数を作ってその中でローカル変数に使いたい値を代入してやるのが一番らくちんかな?
var list = ["hoge", "boe", "moe"];
for(var i in list){
    (function(){
        var tmp = list[i];
        setTimeout(function(){
            alert(tmp);
        }, 5 * 1000);
    })();
}
(function(){})(); でこの外側のfunctionがその場で実行されて、その中の変数tmpはそのつど作られるのでそこに値を代入(参照?)してやればうまくいくとそういう感じ

ページ遷移、リンククリック
locationというオブジェクトに対して操作することで可能
location.reload(); //ページをリロード
location.href = "http://....";  //指定のURLに飛ぶ
var a = document.getElementsByTagName("a")[0];
location.href = a.href; //aタグのリンクに飛ぶ
location.href = a.getAttribute("href"); //これはダメ どうも絶対パスじゃないといけないみたい

さて、ここから先は解決していない問題。
GM_xmlhttpRequestの結果のパース
 GM_xmlhttpRequest({
  method:"GET",
  url:target_url,
  onload:function(x)
  {
                    x.responseText;
                    //code...
                }
 });
とりあえずリクエストを発行するところまではいいんだが、結果のこのx.responseTextの処理の仕方がわからなかった。この結果をdocumentと同じように扱いたいがどうやってその形式にパースしてやるのかわからない。結局responseTextを単なるテキストとして文字列マッチで処理してしまってる。
DOMParserとかを使ってパースするとか書いてあったんだけど、リクエスト先が普通にhtmlでbrタグが閉じられてなかったりとかでまともにパースできない。でもGreasemonkeyで実際にHTMLをパースして結果をdocumentというオブジェクトであらわせてるんだからできると思うんだけどなぁ 分からん。

Arrayクラスのメンバー関数がいろいろ便利
.someとか最初知らなくて普通にforでまわしてフラグ立てたりしてた。
そんな中困ったのが.filterや.sort 関数。配列で飛び飛びの配列ID番号(list[332] = 10; list[567] = 20; みたいな感じ)が入ってるのがあって、そのID番号に意味があるんだけどそいつらを加工するのにfilterやsortを使うと。出来上がる配列がもとにあった飛び飛びのIDがきえちゃってて0,1,2 と詰まった感じのものになってしまう。これIDを引き継いでやる方法ないのかな?

別のJavaScriptで付加されたイベントの実行ができない
input type=button のオブジェクトにたいしてonclickが別JavaScriptで設定されている場合はうまく動いている気がするんだけど、img タグにonclickイベントを負荷した場合にimgタグに対してimg.click(); って呼び出しをしてもonclickイベントが通知されないみたいでコードが実行されずうんともすんとも言わない。これは何で!?結局良くわからなかったので別ソースのJavaScriptのonclick部分をコピペして自分で独自にイベントを追加しちゃいました。何でなんだろう・・・

でもまぁ、なんだかんだ言いながらも作りたいものは作れる状態になってきた。Haskellをやったときのような不自由さは感じないかなぁ。