2011年11月23日

JavaScript非同期ライブラリ PotLite.js 1.22 リリース。アスペクト指向っぽく書けるSignal実装

追記

PotLite.js 1.22 はバギーなため 記事内のリンクを 1.23 に変更しました。

PotLite.js 1.22 リリースしました。

PotLite.js は、非同期処理や負荷のかからないループ処理やイテレータを重視した JavaScript ライブラリです。
経緯などについては、以前の記事 や、 CPU 使用率のベンチマーク結果の記事 を参照ください。

ダウンロード

PotLite.js 最新

レポジトリ


PotLite.js バージョン 1.22 では Pot.Signal オブジェクトを実装しました。

Pot.Signal は、任意のハンドラを登録し シグナルを送信して実行に移すことが可能です。
言い方を変えると、自らイベントを作成し、任意のタイミングで呼び出せるようなものです。

「イベント」という概念で実装したわけではありませんが、ここでは便宜上イベントという言い方をします。

Pot.Signal には大まかに以下の関数があります。

  • attach : イベントを登録
  • detach : イベントを解除
  • signal : イベントを実行

Pot.Signal の使用例

Pot.Signal はいろいろな使い方があります。
例えば、
// イベントを登録
//  (第一引数に文字列を渡すと document.getElementById を使用する)
var handler = attach('#foo', 'click', function(ev) {...});

// イベント解除
detach(handler);
上のように addEventListener/removeEventListener の代わりに使用したり、
var MyObj = {};

// 独自のシグナルを登録
var handler = attach(MyObj, 'clear-data', function() {
  // プロパティの初期化など
  MyObj.data = null;
  MyObj.time = null;
});

attach(window, 'load', function() {
  // 初期化するシグナルを送信
  signal(MyObj, 'clear-data');

  // リセットボタンを押した時にもクリアするよう設定
  attach('#reset', 'click', function() {
    signal(MyObj, 'clear-data');
  });

  // 既存の処理など
  myLoadProcess();
  //...
});
上のように attach, signal を独自のシグナルとして使用したりできます。

そして、attachBefore と attachAfter を使うと処理の前後に何らかの動作を追加できます。
// 保存ボタンを押した時のイベントを設定
attach('#saveData', 'click', function() {
  // 保存する関数
  saveData(document.getElementById('inputText').value);
  // ユーザーに保存を伝える関数
  showSaveData('Saved!');
});

// これにフォーカス移動するよう後付けする
attachAfter('#saveData', 'click', function() {
  document.getElementById('inputText').focus();
});

// その前にログを取るよう設定する
attachBefore('#saveData', 'click', function() {
  MyLogger.log('Save inputText');
});
attachBefore, attachAfter はいくつでも登録できます。
登録した順に実行されます。

また、attachPropBefore, attachPropAfter という関数もあります。
これは、attachBefore, attachAfter と違い、
オブジェクトの関数にダイレクトに作用します。
オブジェクトの持つ関数が呼ばれると、自動的に Before, After のハンドラ関数が実行されます。


例えば何らかのアプリケーションを実行する時に、ログをとりたくなった場合。
var MyApp = {
  execute : function() {
    // 何らかの処理を開始する
    myAppDoit();
  }
  // ...
};

attach('#execute', 'click', function() {
  // アプリケーションを実行
  MyApp.execute();
});

// 実行する前にログを取る
attachPropBefore(MyApp, 'execute', function() {
  MyLogger.log('Begin execute');
});

// 実行した後のログを取る
attachPropAfter(MyApp, 'execute', function() {
  MyLogger.log('End execute');
});
このように設定することで、上の例の場合では
MyApp.execute が実行される前後に任意の処理を追加できます。

解除したくなったら、
var handler = attach(...);

// シグナルを解除
detach(handler);
attach*() 関数の戻り値のハンドラオブジェクトを使い、
detach (Pot.Signal.detach) で解除できます。

他にも detachAll() を使い一括で解除することもできます。


Pot.attach(), Pot.signal() などは Pot.Signal.attach() として実装されていますが、
Pot オブジェクトからも参照できます。
Pot.globalize() を実行しておくと、単に attach(obj, ...) と書けます。
今回の例では attach と表現しています。


これらの関数、とくに Before, After を使うことで、
アスペクト指向 (AOP) っぽいプログラミングが可能となります。

Pot.Signal は Node.js などのサーバでの利用も可能ですが、
Web ブラウザ上、HTML5 での Web Storage (localStorage や sessionStorage) を使う時などに便利と思います。

また、attach 系の関数 (attach*) の後に .once を付けると、一度だけ実行されます。
// 一度クリックしたら解除される
attach.once('#hoge', 'click', function() {...});

Pot.Signal の関数

Pot.Signal.attach(object, signalName, callback[, useCapture])
スロットにシグナルを登録します。 Pot.Signal が保持しているハンドラリストに任意のオブジェクトとコールバック関数を登録します。 引数 object が DOM エレメントだった場合、addEventListener と同じように働きます。 戻り値は、Pot.Signal.Handler のインスタンスです。
Pot.Signal.attachBefore(object, signalName, callback[, useCapture])
スロットに存在するハンドラが呼ばれる前に実行されるシグナルを登録します。 Pot.Signal.attach() で登録したものと同じシグナル signalName で登録した場合、 attach() のハンドラが呼ばれる前に実行されます。 同じシグナルが存在しない場合 signal() によって実行しても何も起きません。 DOM エレメントに対して attach() した場合も同様です。 戻り値は、Pot.Signal.Handler のインスタンスです。
Pot.Signal.attachAfter(object, signalName, callback[, useCapture])
スロットに存在するハンドラが呼ばれた後に実行されるシグナルを登録します。 Pot.Signal.attach() で登録したものと同じシグナル signalName で登録した場合、 attach() のハンドラが呼ばれた後に実行されます。 同じシグナルが存在しない場合 signal() によって実行しても何も起きません。 DOM エレメントに対して attach() した場合も同様です。 戻り値は、Pot.Signal.Handler のインスタンスです。
Pot.Signal.attachPropBefore(object, propName, callback)
object が持つ関数 propName が呼ばれる前に実行されるシグナル callback を登録します。 attachBefore() と違い、Pot.Signal.signal() を呼ばなくても object[propName] が呼ばれた時に自動的に実行されます。 attachPropBefore は、DOM オブジェクトに対しての動作は保証されません。 戻り値は、Pot.Signal.Handler のインスタンスです。
Pot.Signal.attachPropAfter(object, propName, callback)
object が持つ関数 propName が呼ばれた後に実行されるシグナル callback を登録します。 attachAfter() と違い、Pot.Signal.signal() を呼ばなくても object[propName] が呼ばれた時に自動的に実行されます。 attachPropAfter は、DOM オブジェクトに対しての動作は保証されません。 戻り値は、Pot.Signal.Handler のインスタンスです。
Pot.Signal.detach(object[, signalName[, callback[, useCapture]]])
Pot.Signal.attach*() によって登録したシグナルを 1 つ解除します。 attachBefore, attachAfter, attachPropBefore, attachPropAfter で登録したものも同様に解除可能です。 引数に object だけを渡した場合、関連するシグナルが 1 つ解除されます。 引数 signalName, callback を渡すと明確に解除できます。 また、attach() などの戻り値のハンドラを object として渡して解除もできます。 登録と解除の関係は、setTimeout と clearTimeout の関係と同様に扱えます。 DOM エレメントに対しては、 removeEventListener と同様に扱うことができます。 解除成功時に true、失敗時に false が返ります。
Pot.Signal.detachAll([object[, ...signals]])
Pot.Signal.attach*() によって登録したシグナルをすべて解除します。 attachBefore, attachAfter, attachPropBefore, attachPropAfter で登録したものも同様に解除可能です。 引数に object だけを渡した場合、関連するシグナルがすべて解除されます。 引数 signals を渡すと一致するシグナルがすべて解除できます。 シグナル名となる signals は文字列または配列で複数指定できます。
Pot.Signal.signal(object, signalName[, ...args])
Pot.Signal.attach() によって登録したシグナルを実行します。 引数 object と signalName に一致するシグナルがすべて実行されます。 実行される関数には第三引数~以降に任意の引数が渡せます。

PotLite.js

以下は PotLite.js について情報です。
インストールや概要など、不要な場合は読み飛ばしてください。

PotLite.js ダウンロード

最新

レポジトリ

$ git clone git://github.com/polygonplanet/Pot.js

GitHub : polygonplanet/Pot.js

動作環境

以下の Web ブラウザで動作確認済みです。

  • Mozilla Firefox *
  • Internet Explorer 6+
  • Safari *
  • Opera *
  • Google Chrome *

また、以下の環境でも動作するよう設計されています。

  • Greasemonkey (userscript)
  • Mozilla Firefox Add-On (on XUL)
  • Node.js
  • Other non-browser environment

インストール

一般的な方法で動作します。

例:

<script type="text/javascript" src="potlite.min.js"></script>
<!--または-->
<script type="text/javascript" src="http://api.polygonpla.net/js/pot/potlite/1.23/potlite.min.js"></script>

Node.js の場合。

// Example to define Pot object on Node.js.
var Pot = require('./potlite.min.js');
Pot.debug(Pot.VERSION);

Pot.Deferred.begin(function() {
  Pot.debug('Hello Deferred!');
}).then(function() {
  // ...
});
// ...

Greasemonkey (userscript) の例。

// ==UserScript==
// ...
// @require  https://github.com/polygonplanet/Pot.js/raw/master/potlite.min.js
// ...
// ==/UserScript==
Pot.Deferred.begin(function() {
  return Pot.request('http://www.example.com/data.json').then(function(res) {
    return Pot.parseFromJSON(res.responseText);
  });
}).then(function(res) {
  Pot.debug(res);
  // do something...
});
//...
PotLite.js をバージョンを限定して Web から直接読み込みたい場合、
上の GitHub リンクでは常に最新になってしまうため
実装の差異による不具合が発生するかもしれません。
そのため、API サーバを用意しました。
これは、1.23 の部分をリリース済みのバージョンに合わせて変更できます。
レポジトリに (例えば 1.xx と) バージョンをタグ付けした時に、
あわせて API サーバに置くようにしています。

例えば Greasemonkey で 1.23 を使いたい場合、
// ==UserScript==
// ...
// @require  http://api.polygonpla.net/js/pot/potlite/1.23/potlite.min.js
// ...
// ==/UserScript==
と記述できます。

Greasemonkey に限らず script タグからでもなんでも自由に使ってください。



jQuery プラグインとしての例:

// jQuery を読み込んだ後に実行。
Pot.deferrizejQueryAjax();

// Ajax 系の関数が Pot.Deferred を返すようになる
$.getJSON('/hoge.json').then(function(data) {
  alert(data.results[0].text);
}).rescue(function(err) {
  alert('Error! ' + err);
}).ensure(function() {
  return someNextProcess();
});

// エフェクトなどを Deferred 化する 'deferred' が追加される
$('div#hoge').deferred('hide', 'slow').then(function() {
  // ( hide() が終了したあとの処理)
});

Pot.deferrizejQueryAjax() は現状、
ライブラリ側で実行しません。
なので、プラグインを使用する場合は
コードの最初などで実行する必要があります。

リファレンス・マニュアル

より詳しい情報はすべてリファレンスに載っています。
基本的な導入や、各メソッド・関数についても扱っています。

動作テスト

以下のページで動作テストができます。
ページを開くと実装されている主な関数・メソッドを全てテストします。

自動生成されたドキュメント

Closure Compiler によりソースコードから自動生成されたドキュメントです。

殆どの関数ごとにサンプルコードを載せているので、ある程度は参考になると思います。
生成物をすべて確認しているわけではないので、誤認識してる箇所もあるかもしれません。
より詳細な実装などは直接ソースコードを参照ください。




不明な点、要望やバグや感想などありましたら
@polygon_planet や 下のレポジトリから、またはメールでなんでもどぞです。

レポジトリ

2011年11月5日

フリーズやカクカクしない安定した動作のアプリやアドオン、ゲームなどが作れるJavaScriptライブラリ

Web を閲覧していると、突然 CPU 100% になったり
定期的に負荷がかかったりします。

flash などを除くと、それ以外のほとんどは JavaScript による重いループ処理が原因です。
少しずつ改善されてるような気はしますが
おそらくもう何年も前から、この問題がついてまわっていたのではないでしょうか。

そしてこのような負荷は、高スペックなマシンでは気付かないと思います。
開発者やテスターが高スペックな PC で動作確認を行ったことにより、
瞬間的な負荷を見過ごされたまま公開、リリースされてしまうこともあると思います。
それを、低スペックなマシンや iPhone などスマホを使っているユーザーが実行し
負荷に耐え切れずカクカクな描画になってしまったり、
最悪フリーズしてしまう場合もあります。

マシンの性能に限らず、例え高スペックマシンでも
いくつものアプリケーションを常時起動していて
タスクがいっぱいになっているユーザーもこのような事例に該当します。

何らかのアプリケーションを開発する場合、
処理の高速化は常に求められます。
より高速に動作したほうが軽快なのは確かです。

ですがクライアントアプリケーションを作る場合、
CPU 100% 使う処理を 10 秒も 20 秒も続けられてしまったら
それはもう不快でしかありません。
さらにそれが非ブロックでなく実行されてたら
マシンはもうフリーズのような動かない状態になりっぱなしです。

単純に高速を目指すのではなく、UI のことを考え制御を返すことが重要です。
UI を無視してひたすら高速に実行して 5 秒かかる処理だったら、
定期的に UI に制御を返し
例え数秒遅くなってもバックグラウンドで行い CPU 負荷を抑えることが
ユーザーへのストレス軽減として重要と考えています。

さらに非同期で実行することで並列化も可能になり、
逆に本来より速く実行できる可能性もあります。


PotLite.js は、そのような負荷軽減を可能とする JavaScript ライブラリです。
非同期処理をチェインで扱う Deferred オブジェクトを中心に
いろいろなループ処理を非同期で実行できるイテレータを実装しています。

イテレータは PotLite.js バージョン 1.21 現在、
  • forEach
  • repeat
  • forEver
  • iterate
  • items
  • zip
  • map
  • filter
  • reduce
  • every
  • some
が実装されています。
非同期での実行、同期での実行、そして Deferred チェイン上での実行が可能です。

そして、速度指定できるのが特徴の一つです。
Deferred.forEach(obj, function(value, key) {...})
上のコードが forEach (jQuery での jQuery.each のようなもの) の非同期での通常実行になり、
Deferred.forEach.slow(obj, function(value, key) {...})
上のように .slow と速度を明示することができます。
すると各ループをゆっくり目で実行します。

他にも .fast などの指定ができます。
関数内で return wait(1); 等と Pot.Deferred オブジェクトを返すことで
各ループ間でより細かな wait などの調整ができます。
逆に、非同期イテレートをより速く実行したい場合は
Pot.Defered.forEach.ninja(...) と、
最も速く実行するよう定義されている .ninja を指定すると
通常の for 文を単に関数で包んだのと同じくらいの速度で実行できます。

より詳しくは リファレンス を参照してください。


どれくらい変化があるかベンチマークをとってみました。
イテレータ JSON ファイルサイズ CPU 最大使用率 CPU 平均使用率 実行時間 (ms)
for 文 1MB 14.52% 10.61% 1211ms.
jQuery.each 1MB 16.28% 13.53% 1362ms.
Pot.Deferred.forEach 1MB 11.65% 5.53% 1647ms.
for 文 5MB 24.85% 21.71% 4717ms.
jQuery.each 5MB 27.78% 24.29% 5570ms.
Pot.Deferred.forEach 5MB 13.74% 8.61% 8925ms.
for 文 10MB 28.14% 25.92% 10699ms.
jQuery.each 10MB 31.09% 30.77% 15844ms.
Pot.Deferred.forEach 10MB 18.62% 9.77% 38909ms.

同期と非同期の根本的な違いがありますが
この結果は、下記のネストループを Core i3 で 5 回実行した平均結果です。
JSON ファイルは、はてなキーワード一覧 から取得した CSV をキーワードだけの JSON に変換したものです。
10MB はキーワードすべて、5MB は半分で切った JSON ファイル、 1MB はそのまた半分です。

Pot.Deferred.forEach によるイテレータは、処理時間が他と比べ長くなっていますが
CPU 最大使用率、CPU 平均使用率 共に安定しているのがわかります。


CPU グラフも比較してみました。
すべてのキーワードを対象に、Pot.Deferred.forEach (上) と、jQuery.each (下) をそれぞれ実行した結果です。

jQuery.each は、途中で「警告:応答しないスクリプト」のダイアログがでてしまいました。
そのため、本来なら Pot.Deferred.forEach のほうが時間がかかると予想してください。

それでも、Pot.Deferred.forEach では jQuery.each に比べ
瞬間的 (もしくは断続的) な負荷を抑えることができています。
これは、対象のデータがどれほど巨大でも
負荷のかからないイテレートが可能なことを示しています。


なお、上記のベンチマークは
ある程度負荷のかかるループ処理 を対象としています。
PotLite.js のイテレータは、各ループがある程度重いと判断すると CPU 負荷を抑えるよう働きます。
つまり、逆に言うと
瞬時に終わるような小中規模のループでは、他のループと変わらない速度で実行されるということです。

以下に、小規模のループ結果を記します。
イテレータCPU 最大使用率CPU 平均使用率実行時間 (ms)
for 文4.10%3.87%9ms.
jQuery.each5.54%4.72%18ms.
Pot.Deferred.forEach4.71%4.15%19ms.
この結果は、1MB の JSON ファイルを対象に ユニーク処理をなくした実行結果です。
上と同じく 5 回実行した結果の平均になります。

PotLite.js は、jQuery.each とほぼ同じ実行速度で、
ちょうど for 文を関数で包んだものを実行したのと同じくらいの速度で実行できています。

このような毎回の処理が小さなループでは、 for 文などが最も適していますが
適度に各関数を使うことでコストも減少させることができます。

以下は、テストの使用したソースコードです。
// jQuery.each
function benchmark_jQuery() {
  $.getJSON('hatena.keywords.json?callback=?', {
    size : '10mb'
  }, function(data) {
    var results = [];
    var start = +new Date;
    $.each(data.keywords, function(k, word) {
      if (!/[^a-zA-Z0-9_.-]/.test(word)) {
        var uniq = true;
        for (var i = 0; i < results.length; i++) {
          if (word === results[i]) {
            uniq = false;
            break;
          }
        }
        if (uniq) {
          results.push(word);
        }
      }
    });
    $('#result').text((+new Date) - start);
  });
}
巨大なファイルの転送と負荷のテストのため、ソースのみ記します。
// Pot.Deferred.forEach
function benchmark_potlite() {
  begin(function() {
    return jsonp('hatena.keywords.json?callback=?', {
      queryString : {
        size : '10mb'
      }
    }).then(function(res) {
      var results = [];
      var start = now();
      return Deferred.forEach(res.keywords, function(word) {
        if (!/[^a-zA-Z0-9_.-]/.test(word)) {
          var uniq = true;
          for (var i = 0; i < results.length; i++) {
            if (word === results[i]) {
              uniq = false;
              break;
            }
          }
          if (uniq) {
            results.push(word);
          }
        }
      }).then(function() {
        return now() - start;
      });
    }).then(function(time) {
      $('#result').text(time);
    });
  });
}
以下は、小規模のループとしてテストしたソースコードです。
// jQuery.each
function benchmark_jQuery_lite() {
  $.getJSON('hatena.keywords.json?callback=?', {
    size : '1mb'
  }, function(data) {
    var results = [];
    var start = +new Date;
    $.each(data.keywords, function(k, word) {
      if (!/[^a-zA-Z0-9_.-]/.test(word)) {
        results.push(word);
      }
    });
    $('#result').text((+new Date) - start);
  });
}
// Pot.Deferred.forEach
function benchmark_potlite_lite() {
  begin(function() {
    return jsonp('hatena.keywords.json?callback=?', {
      queryString : {
        size : '1mb'
      }
    }).then(function(res) {
      var results = [];
      var start = now();
      return Deferred.forEach(res.keywords, function(word) {
        if (!/[^a-zA-Z0-9_.-]/.test(word)) {
          results.push(word);
        }
      }).then(function() {
        return now() - start;
      });
    }).then(function(time) {
      $('#result').text(time);
    });
  });
}

PotLite.js

PotLite.js は、Pot.js の軽量バージョンです。 非同期処理のオブジェクト/関数だけに絞ったライブラリです。 経緯などは Pot.js に関する以前の記事 を参照ください。

概要

PotLite.js は、非ブロックでの非同期処理を直列的に書けるようにし、 UI や CPU への負担を軽減するループ処理を中心に実装された JavaScript ライブラリです。 MochiKit ライクな Deferred オブジェクトにより 様々なイテレート (forEach, filter, map, repeat, some など) を可能とします。 ※ここでいう MochiKit ライクとは、JSDeferred とは違い 1 つのチェインが 1 つのインスタンスということです。 ※Deferred チェイン は JSDeferred や MochiKit.Async.Deferred と同じ感覚で扱えます。

ダウンロード

最新

レポジトリ

$ git clone git://github.com/polygonplanet/Pot.js
GitHub : polygonplanet/Pot.js

動作環境

以下の Web ブラウザで動作確認済みです。
  • Mozilla Firefox *
  • Internet Explorer 6+
  • Safari *
  • Opera *
  • Google Chrome *
また、以下の環境でも動作するよう設計されています。
  • Greasemonkey (userscript)
  • Mozilla Firefox Add-On (on XUL)
  • Node.js
  • Other non-browser environment

インストール

一般的な方法で動作します。
例:
<script src="potlite.min.js" type="text/javascript">
</script>
Node.js の場合。
// Example to define Pot object on Node.js.
var Pot = require('./potlite.min.js');
Pot.debug(Pot.VERSION);

Pot.Deferred.begin(function() {
  Pot.debug('Hello Deferred!');
}).then(function() {
  // ...
});
// ...
Greasemonkey (userscript) の例。
// ==UserScript==
// ...
// @require  https://github.com/polygonplanet/Pot.js/raw/master/potlite.min.js
// ...
// ==/UserScript==
Pot.Deferred.begin(function() {
  return Pot.request('http://www.example.com/data.json').then(function(res) {
    return Pot.parseFromJSON(res.responseText);
  });
}).then(function(res) {
  Pot.debug(res);
  // do something...
});
//...
PotLite.js をバージョンを限定して Web から直接読み込みたい場合、 上の GitHub リンクでは常に最新になってしまうため 実装の差異による不具合が発生するかもしれません。 そのため、API サーバを用意しました。
これは、1.21 の部分をリリース済みのバージョンに合わせて変更できます。 レポジトリに (例えば 1.22 と) バージョンをタグ付けした時に、 あわせて API サーバに置くようにしています。 例えば Greasemonkey で 1.21 を使いたい場合、
// ==UserScript==
// ...
// @require  http://api.polygonpla.net/js/pot/potlite/1.21/potlite.min.js
// ...
// ==/UserScript==
と記述できます。 Greasemonkey に限らず script タグからでもなんでも自由に使ってください。 ----
jQuery プラグインとしての例:
// jQuery を読み込んだ後に実行。
Pot.deferrizejQueryAjax();

// Ajax 系の関数が Pot.Deferred を返すようになる
$.getJSON('/hoge.json').then(function(data) {
  alert(data.results[0].text);
}).rescue(function(err) {
  alert('Error! ' + err);
}).ensure(function() {
  return someNextProcess();
});

// エフェクトなどを Deferred 化する 'deferred' が追加される
$('div#hoge').deferred('hide', 'slow').then(function() {
  // ( hide() が終了したあとの処理)
});
Pot.deferrizejQueryAjax() は現状、 ライブラリ側で実行しません。 なので、プラグインを使用する場合は コードの最初などで実行する必要があります。

リファレンス・マニュアル

より詳しい情報はすべてリファレンスに載っています。 基本的な導入や、各メソッド・関数についても扱っています。

動作テスト

以下のページで動作テストができます。 ページを開くと実装されている主な関数・メソッドを全てテストします。

自動生成されたドキュメント

Closure Compiler によりソースコードから自動生成されたドキュメントです。
殆どの関数ごとにサンプルコードを載せているので、ある程度は参考になると思います。 生成物をすべて確認しているわけではないので、誤認識してる箇所もあるかもしれません。 より詳細な実装などは直接ソースコードを参照ください。

久しぶりにちょっとがんばって重点をまとめてみました。 これを機に、Pot.js (PotLite.js) で遊んでくれると嬉しいです。 不明な点、要望やバグや感想などありましたら @polygon_planet や 下のレポジトリから、またはメールでなんでもどぞです。

レポジトリ

2011年11月3日

ページ内のDOM要素を削除したり操作できるTomblooパッチ

Firefox アドオン Tombloo のパッチです。

機能概要

ページ内の DOM 要素を削除したりします。
主にウザい要素を消し去るのに便利かも。
マウス下の HTML エレメントや、選択範囲の要素を削除できます。
Ctrl+A (全選択) して選択範囲を消すと何もなくなり寂しい気分が味わえます。

こんなことは Firebug で事足りるのですが、
パネルを出したり戻したりするのすら めんどくさいので作りました。

Adblock など常時使ってる方にはあまり意味ないかもしれません。

コンテキストメニューの 「Tombloo」 から操作できます。

今のところメニューは 2 つだけですが、
なにか思いついたら追加して随時アップデートする予定です。

パッチ (Download or Update):

インストール/アップデート:

上のパッチリンクを
右クリック→「Tombloo」→「Tomblooパッチのインストール」でインストール/アップデート。


※直接右クリックからインストールできない場合:

できない場合は

「Tomblooパッチのインストールに失敗しなくなるパッチ」 のパッチ/記事をインストールもしくは参照ください。

レポジトリ

2011年11月2日

新GoogleReaderで左サイドバー非表示ボタンをつけるGreasemonkey

こないだから新しいインターフェイスになった Google リーダーにまだ慣れない。
f 押すとフルスクリーンになることを知ったのはいいのですが
マウス操作に慣れてたのでどうしようかと。
そのためにジェスチャー拡張したりマウスボタン拡張したりとかしたくなかったから、
とりあえず Greasemonkey で書きました。
左サイドバーをトグル表示 (表示/非表示) するボタンがつくだけのシンプルなスクリプトです。

Download