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]; });