livedoor Reader で既読記事を非表示にする

livedoor Reader で特定のサイトや記事の本文を非表示にする に関して、はてブコメントにて表題のアイデアを提案していただきました。ちょうど、サイトの既読判定をスクリプトで行う手法が考案されて (g:subtech:id:secondlife:20060812:1155379084 id:brazil:20060812:1155388762)、面白いなぁと思っていたところで、絶妙のタイミングでした。

(以下、既読判定の実装について、直接的にははてなアンテナのコードを参考にさせてもらいました)

フィード表示前のフックを使って、既読であればその記事を削除する、という方法を考えてみます。

概要としては:

register_hook("before_printfeed", function(feed) {
  for (var i = 0; i < feed.items.length; i++)
    if (既読判定(feed.items[i].link))
      feed.items.splice(i--, 1);
});

となります。

この既読判定の仕組みについてなんですが、フィードの表示の度に呼び出されるということで、一つの判定オブジェクト (VisitedCheck) を常駐させておくことにします。

動作版:

// ==UserScript==
// @name      LDR Visited Checker
// @namespace http://d.hatena.ne.jp/reinyannyan/20060820/
// @include   http://reader.livedoor.com/reader/*
// @version   0
// ==/UserScript==

(function() {
  // Based on: http://a.hatena.ne.jp/js/VisitedCheck.js
  var VisitedCheck = new (function() {
    this.div = document.createElement("div");
    this.div.className = "visited_check";
    document.body.appendChild(this.div);

    this.check = function(url) {
      if (window.opera) return false;
      this.div.innerHTML = '<a href="'+url+'" class="visited_check">string</a>';
      return this.div.childNodes[0].offsetWidth <= 0;
    }
  });

  LDR_addStyle("div.visited_check", "position:relative !important;top:-10000px !important");
  LDR_addStyle("a.visited_check", "");
  LDR_addStyle("a.visited_check:visited", "display:none !important");

  register_hook("before_printfeed", function(feed) {
    for (var i=0,items=feed.items,len=items.length; i<len; i++)
      if (VisitedCheck.check(items[i].link)) {
        items.splice(i--, 1);
        len--;
      }
  });
})();

(isVisited というメソッドを check に変えてみました。主語と述語の関係が合ってないような気がしたもので)

IE でしかテストしていませんが、with(unsafeWindow){} で全体 (無名関数の内側) を囲えば Firefox でも動作するんじゃないかなと期待します。

ついでに、オン・オフの機能を加えたより完全なバージョンも併記しておきます (これもコード自体はプレーンな IE 用です)。

// ==UserScript==
// @name      LDR Visited Checker
// @namespace http://d.hatena.ne.jp/reinyannyan/20060820/
// @include   http://reader.livedoor.com/reader/*
// @version   1
// ==/UserScript==

(function() {
  var check_visited = false;
  // Based on: http://a.hatena.ne.jp/js/VisitedCheck.js
  var VisitedCheck = new (function() {
    this.div = document.createElement("div");
    this.div.className = "visited_check";
    document.body.appendChild(this.div);

    this.check = function(url) {
      if (window.opera) return false;
      this.div.innerHTML = '<a href="'+url+'" class="visited_check">string</a>';
      return this.div.childNodes[0].offsetWidth <= 0;
    }
  });

  LDR_addStyle("div.visited_check", "position:relative !important;top:-10000px !important");
  LDR_addStyle("a.visited_check", "");
  LDR_addStyle("a.visited_check:visited", "display:none !important");

  register_command("cv|checkvisited", function() {
    check_visited = !check_visited;
    message("VisitedCheck: o"+(check_visited ? "n" : "ff"));

    var sid = State.now_reading;
    if (sid) {
      if (check_visited)
        rewrite_feed();
      else {
        delete get_unread.cache._index["_" + sid];
        State.last_element = null;
        get_unread(sid);
      }
    }
  });

  register_hook("before_printfeed", function(feed) {
    if (!check_visited) return;
    var dels = [];
    for (var i=0,items=feed.items,len=items.length; i<len; i++)
      if (VisitedCheck.check(items[i].link)) {
        dels.push(items[i].title);
        items.splice(i--, 1);
        len--;
      }
    if (dels.length)
      message("Deleted: "+dels.map(function(v){return v.ry(20,"..")}));
  });
})();