理系学生日記

おまえはいつまで学生気分なのか

RILDRize へ進む、戻る機能追加した

Read It Later は 1 画面 10 エントリしか表示できなくて、それ以降のページを表示したいときとかは、基本やっぱしマウス操作になってしまいます。これはメンドい。メンドくさいことで限りある寿命を縮めるわけにはいかない。そういうわけですので、s キーで進む、a キーで戻るという、やっぱし Livedoor Reader をマネた機能を追加しておきました。

http://userscripts.org/scripts/show/92470
https://github.com/kiririmode/rildrize

Read It Later では、HTML の select タグの onchange をフックして、AJAX でページ遷移を実現しています。このとき、ブラウザ上では DOM が再構築されない(?) ぽくて、画面上は表示が変わっているにも関わらず DOM で取得できない(前ページの DOM が取得される) 意味不明な状況になって発狂しそうだったので、createEvent で change イベントを発火させるという美しくない実装になっています。

diff --git a/rildrize.user.js b/rildrize.user.js
index 893e3b2..2452427 100644
--- a/rildrize.user.js
+++ b/rildrize.user.js
@@ -12,6 +12,8 @@
     const KEY_MARK   = 'p';
     const KEY_OPEN   = 'o';
     const KEY_RELOAD = 'r';
+    const KEY_NEXT   = 's';
+    const KEY_PREV   = 'a';

     // class names
     const CLASS_CURRENT = 'rildrize-current';
@@ -32,19 +34,38 @@
         ].join("\n"));

         entryList = getElementsBySelectors('ul#list > li').filter( isUnreadItem );
-        toggleClassName(entryList[currentPos], CLASS_CURRENT);
+        setClassName(entryList[0], CLASS_CURRENT);

         window.addEventListener("keypress", function (evt) {
+            entryList = getElementsBySelectors('ul#list > li').filter( isUnreadItem );
+            if ( document.getElementsByClassName(CLASS_CURRENT).length <= 0 ) {
+                setClassName(entryList[0], CLASS_CURRENT);
+            }
+
             switch( String.fromCharCode(evt.charCode) ) {
               case KEY_DOWN: move(+1);    break;
               case KEY_UP:   move(-1);    break;
               case KEY_MARK: mark();      break;
               case KEY_OPEN: open();      break;
+              case KEY_NEXT: next(+1);    break;
+              case KEY_PREV: next(-1);    break;
               case KEY_RELOAD: window.location.reload(true); break;
             }
+
         }, true );
     }

+    function next(n) {
+        var evt = document.createEvent("HTMLEvents");
+        evt.initEvent("change", true, true);
+
+        var page = document.getElementById("page");
+        page.value = Number(page.value) + n;
+        page.dispatchEvent(evt);
+
+        currentPos = 0;
+    }
+
     function move(n) {
         if ( currentPos + n < 0 || currentPos + n >= entryList.length ) {
             return;
@@ -54,8 +75,8 @@
         currentPos += n;
         var currentItem = entryList[currentPos];

-        toggleClassName(prevItem,    CLASS_CURRENT);
-        toggleClassName(currentItem, CLASS_CURRENT);
+        removeClassName(prevItem,    CLASS_CURRENT);
+        setClassName(currentItem, CLASS_CURRENT);
     }

     function mark() {
@@ -72,14 +93,13 @@

     function open() {
         var currentItem = entryList[currentPos];
-        toggleClassName(currentItem, CLASS_CURRENT);
+        setClassName(currentItem, CLASS_CURRENT);

         pins.open();
-        entryList = getElementsBySelectors('ul#list > li').filter( isUnreadItem );

         currentPos = 0;
         currentItem = entryList[currentPos];
-        toggleClassName(currentItem, CLASS_CURRENT);
+        setClassName(currentItem, CLASS_CURRENT);
     }

     function addGlobalStyle(css) {
@@ -93,6 +113,26 @@
         head.appendChild(style);
     }

+    function setClassName(elem, className) {
+        var classNames = elem.className? elem.className.split(/\s+/) : [];
+
+        if ( contains(classNames, className) ) {
+            return;
+        }
+        classNames.push(className);
+        elem.className = classNames.join(' ');
+    }
+
+    function removeClassName(elem, className) {
+        var classNames = elem.className? elem.className.split(/\s+/) : [];
+
+        if ( ! contains(classNames, className) ) {
+            return;
+        }
+        classNames = classNames.filter( function (c) { return c != className; });
+        elem.className = classNames.join(' ');
+    }
+
     function toggleClassName(elem, className) {
         var classNames = elem.className? elem.className.split(/\s+/) : [];

@@ -170,7 +210,7 @@
             this.idList.splice(0, num).forEach( function(id) {
                 var pin = data[id];
                 window.open(pin.url, pin.title);
-                toggleClassName(pin.node, CLASS_READ);
+                setClassName(pin.node, CLASS_READ);

                 var evt = document.createEvent("MouseEvents");
                 evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null );
@@ -179,6 +219,7 @@
                     return;
                 }
                 nodeList.item(0).dispatchEvent(evt);
+                pin.node.style.display = 'none';

                 delete data[id];
             });