つくった。
概要
今ブラウジング中のページを既にはてなブックマークしているかを表示する UserScript です。
需要
ぼくは、見ているページをブクマするとき、Tombloo という Firefox Addon を使って、はてブと同時に Evernote にも転送しています。
これはなぜかというと、Evernote の全文検索機能を使いたいからですね。ブックマークのタグは忘れるし、表記が揺れるし、大量になればなるほど管理が面倒になります。その点全文検索は非常に楽だ。はてブにも全文検索機能があるけど、あれはみなさんのような金と社会的ステータスを両立しているリッチメンのための機能であって、毎夜奨学金返済にうなされているぼくには高嶺の花です。
ところが Tombloo には、既にブクマ済かどうかを調べる機能はついていません。つまり、同じページを何度も何度も Evernote に転送してしまうというような可能性を捨てきれない。重複はムダです。ムダなのは許してはいけないし、即刻排除すべきだ。
そこで、こういう UserScript を一生懸命つくったわけですね。とてもよかったとおもいます。
実装
実装としては、はてなブックマークエントリー情報取得API を呼び出して、自分のはてな ID のブックマークエントリがあれば Bookmark 済という表示を出しているだけです。
レンダリングの度に当該 API を呼び出すのはムダだし、ムダは排除すべきなので、「b」キーを押したときに API を呼びだすようにしてます。
API 仕様に明記されてないんだけど、一件もブックマークされていない URL に対する API 応答は、GM_xmlhttpRequest の responseText が null になって返ってくるっぽい。気付くまで随分かかった。
追記:
// ==UserScript== // @name AlreadyHatenaBookmarked // @namespace com.kiririmode // @description Have you already hatena-bookmarked this page? // @include http://* // @include https://* // ==/UserScript== (function() { const HATENA_ID = 'your hatena id'; const INITIAL_OPACITY = 0.9; const CSS_DIV_ID = 'AHBdiv'; const CSS_TEXT_ID = 'AHBtext'; const KEY_FIRE = 'b'; init(); function init() { addGlobalStyle([ 'div#' + CSS_DIV_ID + ' { bottom: 20px; right: 0px; position: fixed; width: 300px; height: 20px; background: #00CED1; z-index: 1000 }', 'div#' + CSS_TEXT_ID + ' { color: black }' ].join("\n")); window.addEventListener("keypress", function (evt) { switch( String.fromCharCode(evt.charCode) ) { case KEY_FIRE: fire(); break; } }, true ); } function fire() { var div = document.getElementById(CSS_DIV_ID); if ( div ) { // havs already fired at least once div.style.opacity = INITIAL_OPACITY; fadeout(div); } else { var url = 'http://b.hatena.ne.jp/entry/jsonlite/?url='; url += encodeURIComponent(location.href); GM_xmlhttpRequest({ method: 'GET', url: url, onerror: function(res) { GM_log("error: " + res.statusText); }, onload: function(res) { display(res.responseText); fadeout(document.getElementById(CSS_DIV_ID)); } }); } } function fadeout(elem) { var opacity = elem.style.opacity || INITIAL_OPACITY; opacity -= 0.04; opacity = (opacity > 0)? opacity : 0; elem.style.opacity = opacity; if ( opacity == 0 ) return; window.setTimeout( function() { fadeout(elem); }, 100 ); } function display(responseText){ if ( responseText == 'null' ) { createDisplayBox("not bookmarked"); return; } var obj = JSON.parse(responseText); var bookmarks = obj.bookmarks; for ( var idx = 0; idx < bookmarks.length; idx++ ) { if ( bookmarks[idx].user != HATENA_ID ) { continue; } var msg = bookmarks[idx].timestamp + " already bookmarked: \n" + bookmarks[idx].tags; createDisplayBox(msg); return; } createDisplayBox("not bookmarked"); } function addGlobalStyle(css) { var head = document.getElementsByTagName('head')[0]; if ( !head ) { return; } var style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = css; head.appendChild(style); } function createDisplayBox(msg) { var textnode = document.createTextNode(msg); var textBox = document.createElement("div"); textBox.setAttribute('id', CSS_TEXT_ID); textBox.appendChild(textnode); var divBox = document.createElement("div"); divBox.setAttribute('id', CSS_DIV_ID); divBox.appendChild(textBox); document.body.appendChild(divBox); } })();