RE:Googleリーダー内で、はてなブックマークのコメントを読むためのGreasemonkeyを書いてみた。
このエントリーはテンポラリ用にメモ。
- Googleリーダー内で、はてなブックマークのコメントを読むためのGreasemonkeyを書いてみた。 | ねねとまつの小部屋
入れてみたが動かなかったので修正。
相変わらず面倒なので何もかもそのまま。
- entry-mainにappendしていた箇所をentry-commentsにappendするようにしたのと、タグのみのユーザーを消去。
- ちなみにentry-commentsって何の意味があるのかわかってない。
- 他のグリモンが挿入したタグかと思ってグリモンアドオン停止しても残ってたので使ってみた。
- はてぶAPIのヘルプみたらlite使えって書いてあったからjsonからjsonliteに変更。
あとで気になった部分があったらまた修正しよう。
追記
- とりあえずオートロードするようにした
- ブックマークされてない場合は何も処理をしない、メッセージを表示しないように修正
- コメントがロードされてる場合は何も処理をしないように修正
- オートロードとメッセージ表示のフラグ追加
- 見た目微調整
- はてブコメントの表示非表示ボタン追加
- ショートカットキー(Dキー)にもトグル追加
- やっぱりオートロードフラグはfalseに
- アイコンのサイズがおかしかったので修正
- エントリーを開いた状態のタイトルにはてなスターを表示するように修正(2009/12/09)
- 全文表示とリスト表示で処理を分けた(2009/12/10)
バグともいう。全文だとはてなスターが死ぬほど重い。
// ==UserScript== // @name GoogleReaderHatebuComments // @namespace http://blog.ne2ma2.com/ // @include https://www.google.tld/reader/* // @include http://www.google.tld/reader/* // @description loading Hatena Bookmark Comments on GoogleReader // @version 0.0.1 // thanks: // * google reader full feed changer http://blog.fkoji.com/ // * LDR Hatebu Comments http://www.milk1000.cc/ // ==/UserScript== (function() { var timeID = null; const AUTO_LOAD = true; const FLASH_MESSAGE = false; var style = { container: [ 'color: #444', 'margin: 0 10px' ].join(';'), caption: [ 'border-top: 1px dotted #cbcbcb', 'font-size: 90%' ].join(';'), comments: [ 'list-style-type: none', 'padding: 5px;', 'margin: 0px', 'list-style-type: circle', 'list-style-position: inside', 'font-size: 90%', 'line-height: 150%' ].join(';'), image: [ 'border: 0px', 'vertical-align: middle', 'margin-right: 2px', 'width: 16px', 'height: 16px' ].join(';'), icon: [ 'border: 0px', 'vertical-align: middle', 'padding:1px 12px 1px 16px' // 'margin-right: 2px', ].join(';') }; document.addEventListener( 'keydown', function(event) { var key = String.fromCharCode(event.keyCode); //ブックマークの表示非表示の切り替え if (key.toLowerCase() == 'd') { if(checkXPath('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="true"]')){ setAttribute('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="true"]','value','false'); getCurrentEntry(); }else{ removeElement('//*[@id="current-entry"]//div[@class="gr-hatebu-comment"]'); setAttribute('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="false"]','value','true'); } } }, false ); //ブックマークの表示非表示の切り替え function createViewBookmarkIcon() { var a = document.createElement('a'); a.setAttribute('href', '#'); a.addEventListener("click", function(event){ if(checkXPath('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="true"]')){ setAttribute('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="true"]','value','false'); getCurrentEntry(); }else{ removeElement('//*[@id="current-entry"]//div[@class="gr-hatebu-comment"]'); setAttribute('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="false"]','value','true'); } }, false); var img = document.createElement('img'); img.setAttribute('src', 'http://r.hatena.ne.jp/images/popup.gif'); img.setAttribute('alt', 'view bookmark'); img.setAttribute('title', 'view bookmark'); img.setAttribute('style', style.icon); a.appendChild(img); var node = document.createElement('span'); node.setAttribute('class', 'hatena-bcomment-view-icon'); node.setAttribute('value', 'true'); node.appendChild(a); return node; } function createAddBookmarkIcon() { var url = getFocusedLink(); var title = getFocusedTitle(); var a = document.createElement('a'); a.setAttribute('href', 'http://b.hatena.ne.jp/add?mode=confirm&is_bm=1&title=' + escape(title) + '&url=' + escape(url)); a.setAttribute('target', '_blank'); var img = document.createElement('img'); img.setAttribute('src', 'http://b.hatena.ne.jp/images/append.gif'); img.setAttribute('alt', 'add bookmark'); img.setAttribute('title', 'add bookmark'); img.setAttribute('style', style.icon); a.appendChild(img); var node = document.createElement('span'); node.setAttribute('class', 'hatena-bookmark-icon'); node.appendChild(a); return node; } //ブックマーク関連のアイコンの追加 function addViewBookmarkButton(){ if(!checkXPath('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon"]')){ appendElement('//div[@id="current-entry"]//div[@class="entry-actions"]',createViewBookmarkIcon()); } if(!checkXPath('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bookmark-icon"]')){ appendElement('//div[@id="current-entry"]//div[@class="entry-actions"]',createAddBookmarkIcon()); } } //リクエスト function getCurrentEntry() { var link = getFocusedLink(); if(!link || checkXPath('//*[@id="current-entry"]//div[@class="gr-hatebu-comment"]') || checkXPath('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="true"]')){ return; }; if(w.Hatena && checkXPath('//div[@id="current-entry"]//a[@class="entry-title-link"]') && checkXPath('//*[@id="view-list" and @class="unselectable link link-selected"]')){ new w.Hatena.Star.EntryLoader() } container = document.createElement('div'); container.setAttribute('style', style.container); container.setAttribute('class', 'gr-hatebu-comment'); appendElement('//div[@id="current-entry"]//div[@class="entry-comments"]',container); GM_xmlhttpRequest({ url:'http://b.hatena.ne.jp/entry/jsonlite/?url=' + encodeURIComponent(link), method:"GET", onload:function(xhr){ var obj = eval("(" + xhr.responseText + ")"); var element =createComments(obj); setAttribute('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="true"]','value','false'); } }); } function appendElement(xpath,element){ var currentEntry = document.evaluate(xpath,document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null ); var currentItem = currentEntry.snapshotItem(0); if(currentItem){ currentItem.appendChild(element); } }; function removeElement(xpath){ var currentEntry = document.evaluate(xpath,document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null ); for (var i = 0; i < currentEntry.snapshotLength; i++) { currentEntry.snapshotItem(i).parentNode.removeChild(currentEntry.snapshotItem(i)); } }; function setAttribute(xpath,attr,value){ var currentEntry = document.evaluate(xpath,document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null ); var currentItem = currentEntry.snapshotItem(0); if(currentItem){ currentItem.setAttribute(attr,value); } }; //コメント表示 function createComments(json){ if(!json || !json.count){return;} message('Loading Hatebu Comment...'); var caption, comments; container = document.createElement('div'); caption = document.createElement('div'); caption.setAttribute('style', style.caption); caption.innerHTML = '\u3053\u306e\u30a8\u30f3\u30c8\u30ea\u30fc\u3092\u30d6\u30c3\u30af\u30de\u30fc\u30af\u3057\u3066\u3044\u308b\u30e6\u30fc\u30b6\u30fc (' + json.count + ')'; container.appendChild(caption); comments = document.createElement('ul'); comments.setAttribute('style', style.comments); json.bookmarks.forEach(function(bookmark) { if (bookmark.comment.length != 0){ var comment, image; var date = new Date(bookmark.timestamp); comment = document.createElement('li'); comment.innerHTML = date.getFullYear() + '\u5e74' + (date.getMonth() + 1) + '\u6708' + date.getDate() + '\u65e5 '; image = document.createElement('img'); image.setAttribute('style', style.image); image.src = 'http://www.hatena.ne.jp/users/' + bookmark.user.slice(0, 2) + '/' + bookmark.user + '/profile_s.gif'; comment.appendChild(image); comment.innerHTML += '<a href="http://b.hatena.ne.jp/' + bookmark.user + '">' + bookmark.user + '</a>\u300e'; bookmark.tags.forEach(function(tag) { comment.innerHTML += '[<a href="http://b.hatena.ne.jp/' + bookmark.user + '/' + tag + '">' + tag + '</a>]'; }); comment.innerHTML += bookmark.comment + '\u300f'; comments.appendChild(comment); } }); container.appendChild(comments); appendElement('//div[@id="current-entry"]//div[@class="gr-hatebu-comment"]',container); message('Loading Hatebu Comment... Done'); }; //フォーカスの当たっているリンクを取得 function getFocusedLink() { return getStringByXPath('//div[@id="current-entry"]//a[@class="entry-title-link"]/@href'); }; //フォーカスの当たっているタイトルを取得 function getFocusedTitle() { return getStringByXPath('//div[@id="current-entry"]//h2[@class="entry-title"]'); }; function getStringByXPath(xpath, node) { var node = node || document var doc = node.ownerDocument ? node.ownerDocument : node var str = doc.evaluate(xpath, node, null, XPathResult.STRING_TYPE, null); return (str.stringValue) ? str.stringValue : '' }; function checkXPath(xpath){ var item = document.evaluate(xpath, document, null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); return (item.snapshotLength) ? true : false; } // copied from FLASH KEY (c) id:brazil // http://userscripts.org/scripts/show/11996 // slightly modified. var FlashMessage = new function() { addStyle(<><![CDATA[ #FLASH_MESSAGE { position : fixed; font-size : 200%; z-index : 10000; padding : 20px 50px 20px 50px; left : 1em; top : 1em; background-color : #444; color : #FFF; -moz-border-radius: 0.3em; min-width : 1em; text-align : center; } ]]></>.toString() .replace(/^\ {4}/gm, "")); var opacity = 0.9; var flash = document.createElement('div'); flash.id = 'FLASH_MESSAGE'; hide(flash); document.documentElement.appendChild(flash); var canceler; this.showFlashMessageWindow = function(string, duration) { duration = duration || 400; canceler && canceler(); flash.innerHTML = string; flash.style.MozOpacity = opacity; show(flash); canceler = callLater(function() { canceler = tween(function(value) { flash.style.MozOpacity = opacity * (1 - value); }, 100, 5); }, duration); }; // ----[Utility]------------------------------------------------- function callLater(callback, interval) { var timeoutId = setTimeout(callback, interval); return function() { clearTimeout(timeoutId); }; } function tween(callback, span, count) { count = count || 20; var interval = span / count; var value = 0; var calls = 0; var intervalId = setInterval(function() { callback(calls / count); if (count == calls) { canceler(); return; } calls++; }, interval); var canceler = function() { clearInterval(intervalId); hide(flash); }; return canceler; } function hide(target) { target.style.display = 'none'; } function show(target, style) { target.style.display = style || ''; } }; function message(mes) { //w.message(""); w.message(mes); //console.log(mes); if(FLASH_MESSAGE){ FlashMessage.showFlashMessageWindow(""); FlashMessage.showFlashMessageWindow(mes, 1000); } } // copied from LDRize (c) id:snj14 function addStyle(css, id) { // GM_addStyle is slow var link = document.createElement('link'); link.rel = 'stylesheet'; link.href = 'data:text/css,' + escape(css); document.documentElement.appendChild(link); } if(AUTO_LOAD){ setInterval(getCurrentEntry, 3000); } setInterval(addViewBookmarkButton, 3000); var w = typeof unsafeWindow != "undefined" ? unsafeWindow : window; var initialized = false; var s = document.createElement('script'); s.src = 'http://s.hatena.ne.jp/js/HatenaStar.js'; s.charset = 'utf-8'; document.body.appendChild(s); var t = setInterval(function(){ if(w.Hatena){ // w.Hatena.Star.EntryLoader.headerTagAndClassName = ['h2', 'entry-container']; // w.Hatena.Star.EntryLoader.headerTagAndClassName = ['h2', 'entry-title']; w.Hatena.Star.SiteConfig = { entryNodes: { 'div.entry-container': { uri: 'h2 a', title: 'h2', container: 'h2' } } }; if(checkXPath('//*[@id="view-cards" and @class="unselectable link link-selected"]')){ new w.Hatena.Star.EntryLoader() } clearInterval(t); } }, 3000); })();