ISBN 編集テーブル自動更新

via http://d.hatena.ne.jp/natu_n/20071014/1192365298
natu_n さんの野望を読んでいて、Greasemonkey スクリプトに関してはユーザ側でテーブルをキャッシュするというアプローチもありなんじゃないかと思ったので試してみた。
やりたいことはこんなの。

  • ranges.js から編集テーブルを生成し、ローカルにキャッシュする
    • GM_setValue()、GM_getValue()
  • キャッシュに有効期限(24時間あたり)を設定し、期限まではキャッシュ使う
  • 期限後も ranges.js が更新されてなければキャッシュを使う、期限は延長する
    • レスポンスヘッダのLast-Modified



2007/10/20 追記
If-Modified-Since 使ったほうがよさげ。

AutoPagerize が SITEINFO を読み込むのと同じような感じかな。実装を見てないのでわからないけど。

で、コード。リファクタリング必須です。getIsbnGrpTbl() と updateIsbnGrpTbl() 以外はテスト用にてきとーに。

// ==UserScript==
// @name           testIsbnTblCache
// @namespace      http://d.hatena.ne.jp/hetappi/
// @include        http://www.google.co.jp/
// ==/UserScript==

(function() {
  //
  function getIsbnGrpTbl(callback) {
    GM_log(arguments.callee.name);

    const cache = eval(GM_getValue('isbnGrpTblCache'));
    if (!cache)
      updateIsbnGrpTbl(callback, null);
    else if (cache.expire.getTime() >= Date.now())
      callback(cache.tbl);
    else
      updateIsbnGrpTbl(callback, cache.tbl);
  }

  //
  function updateIsbnGrpTbl(callback, tbl) {
    GM_log(arguments.callee.name);

    GM_xmlhttpRequest({
      method: 'get',
      url: 'http://www.isbn-international.org/converter/ranges.js',
      onerror: function() {
        callback(tbl);
      },
      onload: function(resp) {
        if (!resp.responseHeaders.match(/^Last-Modified:\s(.+)$/m)) {
          callback(tbl);
          return;
        }
        const modified = new Date(RegExp.$1);
        if (tbl && tbl.modified >= modified) {
          callback(tbl);
          return;
        }
        const cache = {
          modified: modified,
          expire: new Date(Date.now() + 24 * 60 * 60 * 1000),
          tbl: {}
        };
        while (
          /gi\.area(\d+)\.text="(.+)";\ngi\.area\d+\.pubrange="([\d\-;]+)";/g.exec(
            resp.responseText)) {
          cache.tbl[RegExp.$1] = {
            name: RegExp.$2,
            ranges: RegExp.$3.split(';').map(
              function(v) { return v.split('-'); }
            )
          };
        }
        GM_setValue('isbnGrpTblCache', cache.toSource());
        callback(cache.tbl);
      }
    });
  }

  //
  function hyphenateIsbn10(isbn10, tbl) {
    GM_log(arguments.callee.name);

    for (var grp in tbl) {
      if (RegExp('^' + grp + '(.+)').test(isbn10)) {
        const rest = RegExp.$1;
        for (var rs, i = 0, max = tbl[grp].ranges.length; i < max; ++i) {
          rs = tbl[grp].ranges[i];
          if (rest > rs[0] && rest < rs[1])
            return [
              grp, rest.substring(0, rs[1].length), rest.substring(rs[1].length, 8), rest[8]
            ].join('-');
        }
        break;
      }
    }
    return null;
  }

  //
  getIsbnGrpTbl(
    function(tbl) {
      ['0596000278', '4061825550', '4873113296'].forEach(
        function(isbn10) {
          GM_log(isbn10 + ' -> ' + hyphenateIsbn10(isbn10, tbl));
        }
      );
    }
  );
})();
...: getIsbnGrpTbl
...: hyphenateIsbn10
...: 0596000278 -> 0-596-00027-8
...: hyphenateIsbn10
...: 4061825550 -> 4-06-182555-0
...: hyphenateIsbn10
...: 4873113296 -> 4-87311-329-6

natu_n さんの真似して、野望(というか妄想?)を書いてみる。Amazon の商品ページから東京都内の図書館の蔵書検索を行う Greasemonkey スクリプト - へたっぴ日記に関して。

  • 上に書いた方法を取り込む
    • 現状は日本の ISBN だけハイフン編集している
  • スクリプト内に定義している検索対象の図書館やオンライン書店の定義を Wiki などに記述して時々ローカルに取り込む
    • id:swdyh さんが shibuya.js で言っていた「プログラム+ Wiki パターン用 Wiki」のその後が気になる
    • ユーザ側で対象毎に on/off 切り替えできるようにする、あぶなそうだしデフォルト off
  • Amazon だけでなく GoogleBooks やオンライン書店、図書館などからも検索できるようにする
    • ISBN を抽出する XPath などをサイトごとに定義する、これも Wiki
  • 所蔵する館名まで表示する
  • 邪魔なときがあるので移動できるようにする
  • もっとあったような気がするけど忘れた