ハイクをキー操作で見られるようにするスクリプト。
各ハイクごとの付加情報や、継ぎ足されたページへのリンクを表示したりもする。
(*)リプライの展開は expandrepliestree.user.js (by cho45 さん)がインストールされてる場合のみ。( Opera と Safari な人は os0x さんの方。)
それからスターコメントが表示されてるときは、 space キーでスクロールできるようになってる。
j と k の機能はそれぞれ f と d でも同じく使えるようにした。個人的な好みで。片手で操作できるように。
オートモード中は自動でスクロールされる。パラパラパラって。上のハイクから下のハイクに移るまでの時間と文字数の差で速度が更新されてる。これは左のメニューから見られる。ただし 500 ms が最短。もう一回 a キーを押すか、 j や k を押すかで止まる。
Firefox の拡張や LDRize を使いたくなかった。なのでこれは LDRize と干渉する。そのあたりは 適当に @exclude とかで…。ゴメンナサイ。
Opera と Safari でも動く。これ大事。けど Safari (の GreaseKit )はロードのタイミングがずれたり、なんかヘンなときが…。(だれか対処法おしえてください ><)
f や d などのキーが気に入らない人は、ソースの setup()
を見てください。
それからイベント駆動な感じの設計になっていて、あとで書き足したりしやすくなってる(と思う)。 setup()
の終わりあたりで w.HatenaRewinder= HR; みたいにすれば、他のスクリプトからも呼べるようになる。
Install Fails: Postflight script doesn't run にあるとおりなんだけど、結論だけ紹介。
あとはもう一度インストールするだけ!
Contact the software manufacturer for assistance.
なんて言われたりしてビビるんだけど気にしない気にしない。
フュージョンすごいよフュージョン
var F= function(fn){ var tmp= function(){ return fn.apply(this,arguments); }; tmp.wrap= function(callback){ var prev= fn; fn= function(){ return callback.apply(prev,arguments); }; }; return tmp; }; var my_func= F(function(str){ return '<'+str+'>'; }); my_func('hogege'); // <hogege> ← 普通に呼べる my_func.wrap(function(str){ return '* '+this(str)+' *'; // いつでも呼べる }); my_func.wrap(function(str){ return '* '+this(str)+' *'; // 何回でも包める }); my_func('fofofo'); // * * <fofofo> * *
元の関数を直接ラップするような書き方ができる。とてもいい。
これだと、一つ一つの関数ごとに wrap を持つことになってしまう。
でも、 Function.prototype はいじりたくない。
call とかそのまま使えるかと思って、やってみた。結論。
var F= function(){}; F.prototype= function(){}; F.prototype.valueOf= function(){ return function(){ alert('valueOf called'); }; }; var tmp= new F; tmp.call(); // valueOf called
valueOf が呼ばれるのは Firefox 限定らしい。
tmp.call は存在するのに、他のブラウザでは Type error で呼ぶことができないという結果。妙だ。残念。
]]>関数 F はプロトタイプに加えておく。便宜上、名前も curry に変更しよう。
var sum= function(){ var result=0; for(var i=0, n=arguments.length; i<n; i++){ result += arguments[i]; } return result; }; Function.prototype.curry= function(args){ var fn= this, self= arguments.callee, args= args || []; return function(){ if( !arguments.length) return fn.apply(this,args); else return [].push.apply(args,arguments), self.call(fn,args); }; }; sum= sum.curry(); sum(1)(2)(3)(); // 6
が、しかし!
関数が定義された時点での引数の個数は、(固定されていれば) this.length から得られるので
var adder= function(a,b,c){ return a+b+c; } Function.prototype.curry= function(args){ var fn= this, self= arguments.callee, args= args || []; return function(){ [].push.apply(args,arguments); if( args.length >= fn.length) return fn.apply(this,args); else return self.call(fn,args); }; }; var add= adder.curry(); add(1)(2)(3); // 6 ← カッコがいらない!
なんて書きかたもできるぜ、 ということを Self-currying JavaScript functions から知った。偶然。おお。
短いコードなのでそのまま引用してしまおう。たぶん問題ないだろう。たぶん…。
Function.prototype.toSelfCurrying = function(n) { n = n || this.length; var method = this; return function() { if (arguments.length >= n) return method.apply(this, arguments); return method.curry.apply(arguments.callee, arguments); }; }; var add = adder.toSelfCurrying(); add(1)(2)(3) // --> 6 add(7,8)(23) // --> 38
ただし、これは prototype.js の Function.prototype.curry を使ってる。こんなやつ。
Function.prototype.curry= function() { if (!arguments.length) return this; var __method = this, args = $A(arguments); return function() { return __method.apply(this, args.concat($A(arguments))); } }
ちなみにこれと同じ、
Function.prototype.curry = function () { var args = arguments; var self = this; return function () { Array.prototype.unshift.apply(arguments, args); return self.apply(this, arguments); }; };
という curry までなら、前に JavaScript でのカリー化が流行ったときに、 nanto_vi さんが書いている 。
カッコをつなげて書けるのが新しい。
参照記事では、これを self-currying と言っていた。
奇しくも、任意の個数の引数に対応する形なら、あの最初に作った「変な関数」は間違いでもなかった。
JavaScript ++
]]>サンプルとして、引数全部を合計して返す関数を使う。
function sum(){ var result=0; for(var i=0, n=arguments.length; i<n; i++){ result += arguments[i]; } return result; } sum(1,2,3,4,5); // 15
これを、
var sum2= F(sum); sum2= sum2(1)(2)(3)(4)(5); sum2(); // 15
と呼べるようになる。引数はいくつでも渡せるから、
var sum3= F(sum); sum3= sum3(1,2,3); sum3= sum3(4,5,6); sum3= sum3(7,8,9); sum3(); // 45
とも書ける。
var F= function(fn){ return function(args){ var self= arguments.callee; return function(){ if(arguments.length){ [].push.apply(args,arguments); return self(args); } else return fn.apply(this,args); } }([]); };
楽しい。何の役に立つかはサッパリわからない。
ページの「戻る」が割り当てられている delete キーをうっかり押したばかりにアワワワワという事態を、これが防ぎます。
また、クリップブラウザ( shift+c で出てくるやつ)でのクリップの削除も出来るようにします。
追記:Sat, 12 Jan 2008 00:52:58 +0900 (JST)
ちなみに Firefox では、 about:config の Browser.backspace_action
を 1 にすれば delete キーを scroll-up に変更できます。
といっても必要なのは、 delete キーのキーコードが 8 であることと、 「ページを戻す」デフォルトイベントは抑止できるということ、 の 2 つだけです。
キー操作で購読停止ができるようになるよ!って書きたかったんだけど、 さっきやってみたら実は fn+delete でも操作できました 。 keyCode が変化してて…(´・ω・`)
まあでもこっちのほうが楽です。
LDR でページを戻すことなんてないので、これは最初から対応してほしいところです。理屈は Safari でも同じなので。
参考に。Sat, 02 Feb 2008 00:32:53 +0900 (JST)
Fastladderのviモードにunsubscribeする"delete"コマンドを追加するuserjs
でも私のほうは Fastladder での動作は確認してませんスミマセン。たぶんスクリプトは同じなので @include の変更だけで動くと思います ><
]]>
// マウスイベントの監視を Autoscroll.start(); // 開始 Autoscroll.stop(); // やめる
shift キーを押しながらマウスポインタを動かすと開始!
手を離せばそのままスクロールされ続ける。 1 ピクセルでも動かせば止まる。
( shift 押しながら)対象の要素の、外側にいくほど速度アップ。
上に挙げたうちの IE を除くブラウザで。
左下にマウスポインタを持っていくとオンとオフが切り替わるようになってる。
Safari や Opera では残念ながら、上のような「模範的な都合のいい例」以外のデザインで、うまくいかないことが多々ありました。 でもせっかくなのでアップしておきます。
他にも、スクロールできそうなところを勝手に最優先するので…まあその、ええと、察して下さい。
body 要素の縦スクロールに限ると話は至極簡単ですが、それでは何もおもしろくないので、たぶん作りかえないと思います。
ctrl 押しで body 要素、 shift ならその他、というように、そのうち操作を分けるかもしれません。それなら煩雑ではありますが、ある程度は確実です。
あの、マウスの真ん中のボタンで、びよーんってできるようになるやつ。
手を離しても自動でスクロールを進めてくれるから、 誰かの tumblr を眺めたり長文のテキスト系サイトを一定速度で読みたいといったときに、 Autopagerize と一緒に使うと便利だった。
でも、ノートパソコンでマウスがないと、できなかった。
かなり(縦に)長めのページでは特に、少しバーを触っただけで画面が移動しすぎるということがなくなって、とてもいい。
上下キーやトラックパッドの二本指でちまちまスクロールしたくなかった。 pageUP/Down でもいいんだけど、それはそれでページの途中で見失うことがあったりした。
(Opera とか)ブラウザによってはデフォルトのオートスクロールが body 要素にしか効かないこともあるので、マウスの繋がってるときでも使えると思う。
(でもレトロなフレームデザインや妙なボックスデザインとかでは、 動いたり動かなかったり…><)
さりげなく LDR と相性がいい気がする。フォルダの中にたくさんフィードがあって手動で一つを選びたいときとか、バババババって滑らかにスクロールできるのが楽しい。
shift 押下で mousemove という操作は、 意外なことにまだ割り当てられていないので、 特に他と干渉するということもない、はず、たぶん。 もし気に入らなかったら、適当にソースの変更を。
関数 check_key()
で例えば、
e.shiftKey && e.ctrlKey
に変えるといったように。
マウスポインタの速度は人によって好みが違うので、
いじる場合には関数 calc()
以下を。
パラメータ化はしてない。逆にコードが汚くなってしまったので。
check_position()
は Safari 対策。
一つ前と比較して位置に変化がないイベントは除外するように。
私の環境では、
mousemove イベントがなぜか shift キーなどで発生してしまっていたので。
check_time()
は、
125 ms 以上の間隔でイベントを処理するための関数。
全ての mousemove イベントでは無駄に計算をしすぎるため。
スクロールの量を可変にする。 マウスポインタの位置で速度が変わるように。
時間の間隔も動的に変わるようにする。数ピクセル単位でゆっくりスクロールさせたくて。 これによって、縦と横とで別々にタイマーを起動させることになった。
スクロールの量がゼロのときにはタイマーが止まるようにした。
IE 7 での動作を確認。ここで ver.1.0.0 に。
バグ修正。対象が html 要素のときは body 要素をスクロールするように変更。
]]>brazil さんの LDR - Drive は本当に便利なのに、 Safari と Opera で使えないのが残念な感じでした。
少しだけです。まずは最初をこんなふうに。
// Opera, Firefox if( typeof Keybind == typeof void 0 ){ window.addEventListener('load', init_ldr_drive, true); } else init_ldr_drive(); // Safari function init_ldr_drive() { var w = typeof unsafeWindow == typeof void 0 ? window: unsafeWindow; w.Keybind.remove('j'); w.Keybind.remove('k'); keyTapper('J', function(e, repeat){ w.Control.scroll_next_item() }); keyTapper('K', function(e, repeat){ w.Control.scroll_prev_item(); }); }
あとは Opera のために、ハッシュの最後の値からカンマを抜いて、forEach を入れかえてください。keyTapper も前に手を加えたコードから変わっているので、新しくこちらを使うのがオススメです。
(また)(勝手に)あげておきました。
Opera なら無名関数でくくるべきです。でも init_ldr_drive なんて関数は必要にならないと思うのでほうっておきます。
Safari では GreaseKit が必要です。スクリプトをロードするタイミングが違うために、あのような分岐が必要となっています。 GreaseKit は load イベントが過ぎてから、各ユーザースクリプトを実行しているようです。
]]>Mon, 26 Nov 2007 01:12:58 +0900
既に id:takef さんが一年以上前に やってました 。 あああああ恥ずかしい。
真摯に BeforeScript イベントでスクリプトを書き換えていて、あちらのほうが楽しいです。
でもこちらの方法なら Safari 3 でも動いたりします。
if(Element.show){ Element.show('tab'); $('preview-tab').style.cursor = 'pointer'; }
これだけで Firefox と全く同じように動きます。
特別な理由もなく Opera を除外していたとしたら、はてなのスタッフは気をつけたほうがいいかもしれませんね。斧やビール瓶が飛んで来ないように。
]]>どっちでも該当
使ってる人結構いると思うんだけどな。ググってみても出てこなかった。 2 年前に書かれたらしいのに。
Opera さびしー。
これだけでいいっぽい。
function GM_getValue( cookieName, oDefault ) { var cookieJar = document.cookie.split( "; " ); for( var x = 0; x < cookieJar.length; x++ ) { var oneCookie = cookieJar[x].split( "=" ); if( oneCookie[0] == escape( cookieName ) ) { // try { // eval('var footm = '+unescape( oneCookie[1] )); // } catch(e) { return oDefault; } // return footm; return unescape(oneCookie[1]); } } return oDefault; }
下手に return unescape(oneCookie[1]) || oDefault;
みたいに書くと空文字を取れなくなる。
これで、存在しないキーのときに undefined
を返してくれる。
わざわざ eval
をあてる必要がよく分かりませんでした。誰か教えてください><
追記 : Tue, 13 Nov 2007 13:54:25 +0900
どこがバグか書いてなかった。
たとえば "foo bar" という文字列を GM_getValue() で取り出そうとすると、
eval
が 'var footm = foo bar'
をパースすることになって、
必ずエラーが発生してた。そのせいでいつも catch 節から oDefault が返されてた、という挙動。
クッキーがドメインで制限されるので、値の出し入れが不便です。
文字列長の上限がひどく低いので、使いどころに困ります。
Opera のシェアが低すぎるので、ほとんどの人には関係ありません。あなかなしや。
]]>
この tz.$F_list
を使って、例えば
var Sequence= tz.$F_list.$struct({ _call_next: function(obj,next,args){ var self= this; setTimeout( function(){ // 関数を実行して、戻り値を「次」に渡す // next(obj.apply(this,args)); でも同じ self.$super._call_next(obj,next,args); },this.interval()); }, interval: function(){ return 500; } });のようなオブジェクトを用意しておけば、
var $n=111; var fn= Sequence.from( function f1(i){ console.log(i); // 111 return 1; }, function f2(i){ console.log(i); // 1 return [2,3,4]; }, function f3(i){ console.log(i); // [2,3,4] } ); // 500ms の間隔を空けて、f1, f2, f3 を順番に実行 fn($n);というように書ける。
上の例までだとそんなに嬉しいことはなくて、次にこう書いてみる。
var Sequence2= tz.$F_list.$struct({ _call_next: function(obj,next,args){ if(typeof obj=='function'){ this.$super._call_next(obj,next,args); } else{ var self= this; setTimeout( function(){ self.$super._call_next(obj.callback,next,args); },obj.interval); } } });これでこんな感じに書けるようになる。中では自動で関数を入れ子にしてくれる。
var tmp= [1,2,3]; var ls= Sequence2.from( { interval: 250, callback: function(i){ console.log(i); // 1,2,3 return 1; } }, function(i){ console.log(i); // 1 return 1.5; }, { interval: 500, callback: function(i){ console.log(i); // 1.5 return 2; } }, { interval: 1000, callback: function(i){ console.log(i); // 2 } } ); ls(tmp);250 ms 後に実行を開始して、 1個目が終わったらすぐに次の関数を実行して、今度は 500ms 待ってから、というぐあいで、あとは、なんとなく分かると思うので省略。一回ずつ間隔を指定できるのと、途中で関数が混ざってても大丈夫になったというのがポイント。
こう書くことで、
var Sequence3= Sequence2.$struct({ foobar: function(){ console.log('foobar'); } });Sequence2 の各メソッドに加えて、 Sequence3.foobar が呼べるようになる。(あまり意味ないけどさ!)
元々は GM_xmlhttpRequest でクロスドメイン通信を多用するときのために考えた。ネストを並列に記述できるから、すごくソースがスッキリする。(でもグローバルにこれを使うとセキュリティが、以下略。)あと GUI のイベント処理とかで、あれが終わるまでこれをロックして次に少し待ったらこっちを呼んで、みたいなパターンも書きやすくなる。
それから Object.prototype
にオブジェクトを足しまくってるとちょっとマズいんだけど、それは省略。
この $struct()
というメソッドを使うと
(コンストラクタじゃなくて)ただのオブジェクトで継承みたいなのができるようになって面白いかも、とか思って始めたんだけど、なんかもう色々…書いてる本人は楽しいんだけど…。
追記:Tue, 06 Nov 2007 21:35:47 +0900
更に追記:Mon, 28 Jan 2008 19:48:49 +0900 (JST)
前にちょっとマズいとか書いたけど、そんなにマズくなかったです。m(_ _)m
それから、 setTimeout()
や GM_xmlhttpRequest()
などの関数スタックは他のと少し違うので、
関数の中から再度これらを呼び出すことで 1つの連続した関数を生成するという案そのものは、
悪くはないと思います。たぶん。
Opera で動かなかったため、 forEach
などを書き換えて 5行ほど追加した。
完全な対応ではない。そこが移植ではなくエミュレートの由縁。リピート開始までのラグが異なる。
追記 : Thu, 01 Nov 2007 11:04:36 +0900
本家では、
40msぐらいにリピート間隔を縮めると、1度押しただけで連打になってしまうことがあったためリピートが始まるまでの時間を「デフォルトどおりの動作」にしているが、 これが再現できなかったので固定してしまった。 不恰好だが更新前よりは近い挙動になっている。 40ms や 16ms といった短い時間での予期しない連打は、やはり防ぐに越したことはない。
これはリピート間隔の変更に加えて、リピートができること自体にも、実は価値がある。Opera ではキーを押しっぱなしにしても、 keydown ではイベントの実行は一回きりだからだ。では keypress を用いればいいかというと、そうでもない。keypress では例えば r キーと F3 キーとを区別できないのだ。(ちなみに Firefox では e.keyCode と e.charCode との違いによって可能。)しかし一方で keypress を避ければ、今度は * などの shift+ で入力するキーを検出できなくなる。堂々巡りだ。
ここでシンプルな、しかし強力な解決策がある。 keyTapper のとった、キーコードの直書きとタイマー処理だ。これで無事に keydown に絞れる。
ただし、まだオチがある。このままでは US 配列において拡張キーが機能しないのだ。 キーボードの配列ごとにマッピングする必要が生じる。また、ここまで書いて今更だが、keypress 無しにはデフォルトのイベントを抑止できないため、F5 , a, s などでは意図したようには機能しないはずである。
キーイベントの統一的な解決策はない。現状、この keyTapper は素晴らしい。
]]>→ この一覧ページ。数は揃っていても、ありすぎてよく分からない。
で、次の条件のいずれかに該当するようなデザインは、見ないことにした。
主なテキストを含む要素の、
全て探すことには変わりないんだけど、これだけでも半分くらいが消えて、だいぶ手間が省ける。楽々♪
見てのとおり、クリックすると 20個から 7個まで候補を減らしてくれる。コーヒーでもすすりながらのんびり待ちましょう。
Firefox2 と Opera9 で。
一覧を読み込むとページの見出し部分に extract というボタンが現れるので、それを押せば OK。
一画面には 20 のデザインがあって、それを 3秒間隔で開いていく。 開いたら上のチェックにかけて、当てはまればその直後に勝手に閉じる。あとは残ったサンプルに目を通せばいい。
ポップアップだから、「タブで開く」オプションをチェックしておかないと大変なことになる、かも。
ちなみに Opera で No images を設定すれば、かなりサクサク走る。あとは Shift+I で適当に。
setInterval の引数を変えれば 3 秒より短くすることは簡単ですが、あまりやりすぎるとプチ dos アタックみたいになるので、やめておいたほうがいいと思います。
]]>Google Notebook を利用したアプリケーションを設計するプログラマへ。
このドキュメントは、あくまで developer's guide を前提としたリファレンスであり、さらに広範には Google data APIs プロトコルの概念を背景としたものである。
Google Notebook は公開データにアクセスするために、2種類のフィードを用意している。
1 つは、特定のユーザを単位にしてノートブックの一覧を得るための、メタフィードである。
この中で、それぞれのエントリが各々のノートブックに対応する。
このフィードは以下の URI をとる。
GET http://www.google.com/notebook/feeds/userID
もう 1 つは個々のノートブックを単位とするものである。これはノートというエントリによって構成され、ノートごとに全てのメタデータ( URL や作成日など)が含まれている。
URI は以下の形式である。
GET http://www.google.com/notebook/feeds/userID/notebooks/notebookID
Google Notebook は両方のフィードで、以下に示す GData 標準のパラメータに対応している。
さらにノートブック単位のフィードにおいては、
GData Protocol Reference のカテゴリ指定が可能である。ここでいうカテゴリとはノートブックのセクション ( section ) にあたり、例えば "Netherfield Park"
というタイトルのセクションを取り出したいのなら、以下のような URL となる。
http://www.google.com/notebook/feeds /userID/notebooks/notebookID/-/Netherfield%20Park
エントリの作成者 ( author
) と全文検索 ( q
) のクエリには対応していない。ちなみに「作成者」は意味を成さない。なぜなら他のユーザと共有することはできても、公開したノートブックの所有者はただ一人となるためである。ノートブック内の全てのエントリについて、作成者は同一である。
標準パラメータの詳細については GData Protocol Reference を参照のこと。
以上に加えて、 Notebook data API ではもう 1 つパラメータが存在する。
orderby
orderby
パラメータに値 position
を指定することで、
ソートをノートブック内のノートと同じ順序にすることができる。
Google Notebook data API は GData 標準の要素のみを用いている。 更なる詳細は Atom の仕様と、 Common Elements document を参照されたい。
]]>ちょっと便利かもしれない .user.js にコピペシリーズ第 2 弾。
var ul= document.body.appendChild(document.createElement('ul')); ul.setAttribute('class','menu'); var li= ul.appendChild(document.createElement('li')); li.setAttribute('class','item'); li.appendChild(document.createTextNode('textA')); li.appeeeeくぁwせdr!!!
var tmp= ['ul class=menu', ['li class=item', 'textA', ['a href=exampleA.php class=sample id=foo1', 'hoge1'] ], ['li class=item', ['a href=exampleB.html class=sample id=foo2', 'hoge2'] ], ['li class=item', 'text1', ['br'], ['a href=/foo/exampleC1 class=sample id=foo3-1', 'hoge3-1'], ['a href=/bar/exampleC2 class=sample id=foo3-2', 'hoge3-2'], 'text2', 'text3', ['a href= exampleC3 class = sample id = foo3-3', 'hoge3-3'] ], ]; var ul= to.element(tmp); document.body.appendChild(ul);
実にコンパクト。相互再帰。
var to={ element: function(arr){ var str=arr[0], extract= function(reg1,reg2,tmp){ str=str.replace( reg1, function(tgt){ return tmp=tgt,''}); return tmp.match(reg2); }, elem= document.createElement( extract(/^\s*\S+\s*/, /\S+/)); for(var val,attr; str.length; elem.setAttribute(attr,val)){ val= extract(/[^=]*$/, /[^\s].*[^\s]/); attr= extract(/\S+\s*=$/, /[^\s^=]*/); } return this.children( arr.slice(1), elem); }, children: function(arr,to){ (typeof to !='object') && (to=document.createElement(to||'div')) for(var i=0; i<arr.length; i++) to.appendChild( typeof arr[i]=='string'? document.createTextNode(arr[i]): this.element(arr[i]) ); return to; } }
var tmp= [ 'hoge', ['a href=http://example.com/','click here!'], 'hoge2' ]; var span= to.children(tmp,'span');
to.children()
の第2引数には、親ノードか、もしくは親ノードとなる要素のタグ名を渡す。
省略すると div
要素が作られる。
この例では、[ テキスト hoge、要素 a、テキスト hoge2 ] の3つを子要素とする、 span 要素を返す。
属性とその値の区切りに空白を用いた。引用符のエスケープの手間を省きたかったため。
このせいで属性値に空白とイコールを含められない。つまり例えばクラス名の複数指定ができない。
入力の手間を省くだけなら、 「DOMコードジェネレータ @ZEROBASE BLOG」で既出。
でもやっぱり、あれはソースの見た目がちょっと。。。
ちゃんと真面目に作るなら、 JSON で汎用的に書いたり テンプレートエンジンを使ったりするんだろうけど、
ユーザースクリプトでそんな大げさなことしたくない。あるいは、
簡単なメニューを足したいだけなんだけど、 innerHTML
で直書きはしたくない。
でも DOM はめんどい。もっと構造を見やすくしたい。みたいな人に、どうぞ。