livedoor Reader でフィード一覧を絞り込み表示 [更新:20061115]

[20070223] 再アップデート版をファイルとして公開しました。id:reinyannyan:20070223:p1 をご覧下さい。


id:reinyannyan:20061006:p1 で実験的に作ったフィード一覧絞込みスクリプトを改善しました。フォルダ単位でのフィルタリングの他に、任意の JavaScript 式を条件として与えられるようになっています。また、"!" 以外にも

  • !+
  • !-
  • !#

のコマンドを追加しました。


未読フィードが大量にある時に、今読みたいものを確実に効率よく読めるように、と考えて作りました。

特に、フィード一覧の表示数の上限を設定していて、未読フィードがそれよりもたくさん存在する場合に、絞込みを行うことで望みのフィードを漏れなく表示することが (ほぼ確実に) 可能になります。


使用例:

:! +news +tech [CR]          // フォルダ "news" と "tech" のフィードを表示
:!+ news tech [CR]           // 同上
:! -news [CR]                // フォルダ "news" のフィードを消去
:! +tech #{ rate >= 4 } [CR] // "tech" フォルダのうち、レートが 4 以上のフィードを表示
:! [CR]                      // 元に戻す


Tips:

  • "+" と "-" の条件で指定するフォルダ名は、内部的に正規表現として扱われます (例えば "-." は「未分類」の意味になります)
  • 絞込み条件は一度に複数指定できます; 適用順は "+" "-" "#" です
  • "+" の条件では、取得された全ての購読を対象に絞込みが行われます
  • "+" 以外の条件では、現在 (直前) の絞込み状態に対して絞込みが行われます
  • "rate" 以外のフィード情報:
    • "unread_count" (未読数)
    • "subscribers_count" (購読者数)
    • "modified_on" (最終更新時刻 (Unix time))
    • "subscribe_id" (フィード ID)
    • "link" (サイトURL)
    • "feedlink" (フィードURL)
    • "folder" (フォルダ名)
    • "title" (タイトル)
    • "icon" (画像URL)

フォルダを活用していない方でも、「レートの高いものだけ」とか、「未読数の多いものだけ」など自在に絞り込むことで、迅速で効率的な未読処理が可能になるのではと思います。


スクリプト:

// ==UserScript==
// @name         LDR Subscription Filter
// @namespace    http://d.hatena.ne.jp/reinyannyan/20061013/p1
// @include      http://reader.livedoor.com/reader/*
// ==/UserScript==

(function(){with(unsafeWindow){
  var fmodel; // model with custom filter applied
  var silent = false;

  function args2str(args) {
    return Array.from(args).join(" ");
  }

  function joinp(ary, sep) {
    return sep+ary.join(sep);
  }

  function show(data, desc) {
    // Preserve the current state of the list (for further subtraction)
    fmodel = data;
    if (silent) return desc;

    subs.view.clear();
    subs.view.setClass(Config.view_mode);
    subs.view.print(SF[Config.view_mode](data));

    $("subs_container").scrollLeft = 0;
    State.now_reading && set_focus(State.now_reading);
    subs.update_order();

    update("right_bottom_navi", "right_top_navi");
    message("Filtered subs:"+(desc||""));
  }

  register_hook("after_subs_load", function() {
    fmodel = null;
  });

  register_command("!|filter", function() {
    var OKs = [], NGs = [], EXs = [];
    args2str(arguments).replace(/([-+#])((?:{.+?}|\S+))/g,
      function(_, sign, v) {
        (sign == "+" && OKs || sign == "-" && NGs || EXs).push(v);
      });

    if (!(NGs.length || OKs.length || EXs.length))
      return show(subs.model);

    var applied = [];
    silent = true;
    try {
      OKs.length && applied.push(vi["!+"].apply(null, OKs));
      NGs.length && applied.push(vi["!-"].apply(null, NGs));
      EXs.length && applied.push(vi["!#"].apply(null, EXs));
    }
    catch (e) {
      // User might have given incorrect #{} expression
    }
    finally {
      silent = false;
    }
    fmodel && show(fmodel, applied);
  });
  register_command("!+", function() {
    var fs = Array.from(arguments);
    if (!fs.length) return;

    var _model = new Subscribe.Model;
    _model.load(subs.model.list.filter(function(v){
      return fs.some(function(x){return v.folder.match(x)});
    }));
    return show(_model, joinp(fs," +"));
  });
  register_command("!-", function() {
    var fs = Array.from(arguments);
    if (!fs.length) return;

    var _model = new Subscribe.Model;
    // Perform subtraction from the current data set
    _model.load((fmodel||subs.model).list.filter(function(v){
      return !fs.some(function(x){return v.folder.match(x)});
    }));
    return show(_model, joinp(fs," -"));
  });
  register_command("!#", function() {
    var fs = [];
    var ex = args2str(arguments);
    ex.replace(/{\s*(.+?)\s*}/g, function(_, v){
      fs.push(v);
    });
    if (!fs.length)
      fs.push(ex);

    var _model = new Subscribe.Model;
    _model.load((fmodel||subs.model).list.filter(function(v){
      return fs.every/*some*/(function(x){with(v){return eval(x)}});
    }));
    return show(_model, joinp(fs," #"));
  });
}})();

(いつもながら、プレーンな IE で動かしているものに with(unsafeWindow) を被せただけです。不具合がありましたらお知らせください)

どうぞご利用ください。


追記 [20061017]:

複数タイプの条件を与えた場合の表示処理を迅速化しました。