これは何?

ハイクをキー操作で見られるようにするスクリプト。

各ハイクごとの付加情報や、継ぎ足されたページへのリンクを表示したりもする。

こんなやつ。

ダウンロード

haiku_rewinder.user.js

使い方

  • j : 下のハイクにフォーカス
  • k : 上のハイクにフォーカス
  • z : メニューの出し入れ
  • e : リプライの展開(*)
  • c : スターコメントの表示・非表示
  • a : オートモード

(*)リプライの展開は 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; みたいにすれば、他のスクリプトからも呼べるようになる。

2008年03月16日 18:40

VMware Fusion のインストールで失敗した

Install Fails: Postflight script doesn't run にあるとおりなんだけど、結論だけ紹介。

  1. VMware Fusion をアンインストール
    ( /Library/Application Support/VMware Fusion/Uninstall VMware Fusion )
  2. フォルダ /Library/Application Support/VMware Fusion を削除
  3. ファイル ~/Library/Preferences/com.vmware.fusion.plist を削除
  4. フォルダ ~/Library/Preferences/VMware Fusion を削除
  5. 再起動

あとはもう一度インストールするだけ!

Contact the software manufacturer for assistance.

なんて言われたりしてビビるんだけど気にしない気にしない。

フュージョンすごいよフュージョン

参考

http://twitter.com/poafag/statuses/710061482

2008年02月15日 02:50

サンプル

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 で呼ぶことができないという結果。妙だ。残念。

2008年02月10日 02:10
以前作った関数の続き。

カリー化との絡みがおもしろかったので追加記事

関数 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 ++

2008年02月08日 12:00

サンプルとして、引数全部を合計して返す関数を使う。

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);
        }
    }([]);
};

まとめ

楽しい。何の役に立つかはサッパリわからない。

補足記事

self-currying

2008年01月28日 19:47

Mac の Opera で LDR を使ってるみなさんへ

ページの「戻る」が割り当てられている delete キーをうっかり押したばかりにアワワワワという事態を、これが防ぎます。

また、クリップブラウザ( shift+c で出てくるやつ)でのクリップの削除も出来るようにします。

追記:Sat, 12 Jan 2008 00:52:58 +0900 (JST)

ちなみに Firefox では、 about:config の Browser.backspace_action を 1 にすれば delete キーを scroll-up に変更できます。

ユーザースクリプト

ldr_key_patch.js

といっても必要なのは、 delete キーのキーコードが 8 であることと、 「ページを戻す」デフォルトイベントは抑止できるということ、 の 2 つだけです。

最初は

キー操作で購読停止ができるようになるよ!って書きたかったんだけど、 さっきやってみたら実は fn+delete でも操作できました 。 keyCode が変化してて…(´・ω・`)

まあでもこっちのほうが楽です。

LDR でページを戻すことなんてないので、これは最初から対応してほしいところです。理屈は Safari でも同じなので。

ふたたび追記

参考に。Sat, 02 Feb 2008 00:32:53 +0900 (JST)

Fastladderのviモードにunsubscribeする"delete"コマンドを追加するuserjs

でも私のほうは Fastladder での動作は確認してませんスミマセン。たぶんスクリプトは同じなので @include の変更だけで動くと思います ><

2008年01月10日 00:34

つかいかた

// マウスイベントの監視を
Autoscroll.start(); // 開始
Autoscroll.stop(); // やめる

動作確認

  • Opera 9
  • Firefox 2
  • Safari 3
  • IE 6 / 7

デモ

sample_autoscroll.html

shift キーを押しながらマウスポインタを動かすと開始!

手を離せばそのままスクロールされ続ける。 1 ピクセルでも動かせば止まる。

( shift 押しながら)対象の要素の、外側にいくほど速度アップ。

ソース

Autoscroll.js

グリモン

autoscroll.user.js

上に挙げたうちの 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 要素をスクロールするように変更。

2007年12月27日 18:34

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 も前に手を加えたコードから変わっているので、新しくこちらを使うのがオススメです。

ソース

ldrdrive.user.js

(また)(勝手に)あげておきました。

ひとことふたこと

Opera なら無名関数でくくるべきです。でも init_ldr_drive なんて関数は必要にならないと思うのでほうっておきます。

Safari では GreaseKit が必要です。スクリプトをロードするタイミングが違うために、あのような分岐が必要となっています。 GreaseKit は load イベントが過ぎてから、各ユーザースクリプトを実行しているようです。

2007年12月08日 23:57

追記

Mon, 26 Nov 2007 01:12:58 +0900

既に id:takef さんが一年以上前に やってました 。 あああああ恥ずかしい。

真摯に BeforeScript イベントでスクリプトを書き換えていて、あちらのほうが楽しいです。

さらに追記 : Thu, 27 Dec 2007 17:40:57 +0900 (JST)

でもこちらの方法なら Safari 3 でも動いたりします。

ソース

if(Element.show){
   Element.show('tab');
   $('preview-tab').style.cursor = 'pointer';
}

これだけで Firefox と全く同じように動きます。

ダウンロード

hateda_preview.user.js

もしも

特別な理由もなく Opera を除外していたとしたら、はてなのスタッフは気をつけたほうがいいかもしれませんね。斧やビール瓶が飛んで来ないように。

2007年11月23日 22:04

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 のシェアが低すぎるので、ほとんどの人には関係ありません。あなかなしや。

2007年11月13日 13:20

こんなふうに書けるようになるよ

この 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

tz.Struct_0.js

まとめ

それから、 setTimeout()GM_xmlhttpRequest() などの関数スタックは他のと少し違うので、 関数の中から再度これらを呼び出すことで 1つの連続した関数を生成するという案そのものは、 悪くはないと思います。たぶん。

2007年11月04日 17:27