ようやくできたかも・・・
まだ人柱バージョンですが、たぶん動きます。
概要は、こちらを参照。
にしても、ハテナってフィイルアップロードできないのね。
orz......
// ==UserScript== // @name hatebu_switch_a1 // @namespace http://d.hatena.ne.jp/T-miura/ // @include http://b.hatena.ne.jp/hotentry* // @include http://www.google.co.jp/search* // ==/UserScript== /** * 概要: * * 未読、既読の情報によって、 * 表示する情報を制御するスクリプトです。 * * 前見た情報をもう一度探したい(既読のみを表示)、 * 既読のリンクは表示して欲しくない * * * といった場合に使用します *(ブラウザの履歴使っているだけなのでPC変ると終了ですが)。 * * 現在、対応しているサイトは * * ・はてブのホットエントリ、 * ・Googleの検索結果 * * だけですが、PageController.getSite * をいじれば簡単に対応サイトを増やせると思います。 * */ // ログがうざい時に var debug =false; var logon =false; console.debug = console.log; if(!logon){ console.log = function () {}; if(!debug){ console.debug = function () {}; } } //--------------------------------- /** * サイト内のリンク */ function Link(){ this.url; this.visited; this.viewNode; } /** * サイトごとの設定。 * * * @return */ var Site = function () { this.initialize.apply(this, arguments); }; Site.prototype = { initialize: function () { this.url; this.judgeXpath this.ViewElmXpath1 this.ViewElmXpath2 this.visitedColor; //CSS変更用のメソッド。 // changeOn,changeOff,changeAllOffのどれかが入る this.judgeCSSChange; this.links =[]; }, //既読を消す。メソッド名微妙 changeOn: function (linkVisited) { return ( linkVisited); }, //未読を消す。メソッド名微妙・・・ changeOff: function (linkVisited) { return (!linkVisited); }, //機能自体を切る。全部表示。メソッド名微妙・・・ changeAllOff: function (linkVisited) { return (false); }, //設定のロード load:function(){ //TODO フラグとかでやったほうがいいかも。 // こっちのほうがソース簡単で、汎用性高いけど var s= GM_getValue("mode."+this.url); if(s !=null){ this.judgeCSSChange=eval(s ); } }, //設定のSAVE save:function(){ console.log(this.judgeCSSChange); GM_setValue("mode."+this.url,this.judgeCSSChange.toSource()); }, /** * 判定対象とするリンクをかき集めます。 */ getLinks:function () { var self=this; var nodesSnapshot =document.evaluate(self.judgeXpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null ); console.log('getLinks:'+self.judgeXpath+' :size='+nodesSnapshot.snapshotLength); for ( var i=0 ; i < nodesSnapshot.snapshotLength; i++ ) { var tmpUrl=nodesSnapshot.snapshotItem(i).textContent; if(self.links[tmpUrl] ==null){ self.links[tmpUrl]=new Link(); self.links[tmpUrl].url=tmpUrl; } console.debug('getLinks:'+tmpUrl+":"+self.links[tmpUrl]); } console.log('getLinks:'+self.links.length); }, /** * Linkの連想配列を走査し、訪問済みか確認します。 */ checkVisited: function () { var self=this; console.log( 'checkVisited:'+self.links.length); var element = document.createElement('div'); element.id = 'tempFooter001'; document.lastChild.appendChild(element); for ( var i in self.links ) { var link = document.createElement("a"); link.id = "idtempstyle" + i; link.href = self.links[i].url; link.innerHTML = self.links[i].url; document.getElementById('tempFooter001').appendChild(link); var color = document.defaultView.getComputedStyle(link,null).getPropertyValue("color"); document.getElementById('tempFooter001').removeChild(link); console.debug( '4:checkVisited:'+color+':'); if (color == self.visitedColor) { self.links[i].visited=true; } else { self.links[i].visited=false; } console.debug( ' '+i+":"+self.links[i].url+":"+self.links[i].visited); } }, /** * 訪問済みかどうかの情報を使い * viewをいじります。 */ view:function () { var self=this; console.log("view:"+self.links.length); /* Loop through each URL */ for (var i in self.links) { if( self.links[i].viewNode ==null){ var path=self.ViewElmXpath1 + self.links[i].url +self.ViewElmXpath2; var nodesSnapshot =document.evaluate( path , document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null ); this.links[i].viewNode =nodesSnapshot.snapshotItem(0); if(! this.links[i].viewNode){ console.log("can't get ViewNode:"+self.links[i].url); continue; } } console.debug("view:"+self.links[i].url+":"+self.links[i].visited); /* 訪問したことのある要素の状態を変更します。 */ if ( self.judgeCSSChange ( self.links[i].visited ) ){ // visited console.debug( 'change css none:' +self.links[i].url); self.links[i].viewNode.style.display='none'; }else{ console.debug( 'change css display:' +self.links[i].url); console.debug( 'nodesSnapshot:' + self.links[i].viewNode); self.links[i].viewNode.style.display=''; } } }, } // ------------------------------------------------------------------ /** * 基点となるクラス。 * UI部分を提供。 * * PageController > Site > Link * * という形で処理を委譲 * * */ var PageController = function () { this.initialize.apply(this, arguments); }; PageController.prototype = { initialize: function () { this.opened = false; this.container = null; this.closeTimer = null; this.update(); this.site = this.getSite(); // getLinks,checkVisited,viewは今は // セットのように扱っているけど、 // 前は、呼び出すタイミングを分けていた。 this.site.getLinks() ; this.site.checkVisited() ; this.site.load(); this.site.view(); this.registReloadEvent(); }, // サイト(はてぶ、Google等)の解析のために必要なデータを持つクラス // このコントローラで制御対象のサイトを取得する getSite: function (){ var sites =new Array(1); // ハテブ用定義 var site1 = new Site(); // まず、対象とするサイト site1.url='http://b.hatena.ne.jp/hotentry'; // 既読、未読を判定したいURL site1.judgeXpath='//div[@class="entry-body"]/h3/a/@href' // 上記URLを表示しているDIV、ブロック // この単位で表示をいじる。 // なお、上記のURLでパスは引っ掛ける site1.ViewElmXpath1='//div[@class="entry-body"]/h3/a[@href=\'' // こっちは、URL後の部分のXPATH site1.ViewElmXpath2='\']/../../..'; // サイトによって既読のカラーが違うっぽいので持つことに // CSSからひっぱてきたほうがいいのか・・・? site1.visitedColor='rgb(136, 51, 136)'; //デフォルトのモード site1.judgeCSSChange =site1.changeOn; sites[0]=site1; // Google用 var site2 = new Site(); site2.url='http://www.google.co.jp/search'; site2.judgeXpath='//a[@class="l"]/@href' site2.ViewElmXpath1='//li[@class="g"]/h3/a[@href=\'' site2.ViewElmXpath2='\']/../..'; site2.visitedColor='rgb(85, 26, 139)'; site2.judgeCSSChange =site1.changeOn; sites[1]=site2; // マッチするサイトを返す for(var i=0 ; i < sites.length; i++ ){ var b =location.href.match(sites[i].url ); if(b){ console.log('getSite!!'+sites[i].url); sites[i].load(); return(sites[i] ); }else{ console.log('nonSite!!'+sites[i].url); } } return(null); }, open: function () { if (this.container) { this.opened = true; this.container.className = "playlist-controller"; } }, close: function () { if (this.container) { this.opened = false; this.container.className = "playlist-controller closed"; } }, registReloadEvent: function () { console.log("registReloadEvent "); var self= this; document.addEventListener('DOMNodeInserted', function (evt) { //オートページャ用にイベントをみるつもりだったが、 //かなりやばい動きをする場合があるので中止。 //表示カットによって、連続で10ページ //オートページャ でロードされたりする。 //self.site.getLinks() ; //self.site.checkVisited() ; }, false); }, cancelClosing: function () { if (this.closeTimer) { clearTimeout(this.closeTimer); this.closeTimer = null; } }, closeLater: function (msec) { var self = this; this.cancelClosing(); this.closeTimer = setTimeout(function () { self.closeTimer = null; self.close(); }, msec || 300); }, update: function () { var self = this; var listScrollTop = 0; var box = document.getElementById("pageController" ); if (!box) { box = document.createElement("div"); box.id = "pageController" ; box.className = "playlist-controller" + (this.opened ? "" : " closed"); Util.observe(box, "mousemove", function (ev) { if (!self.opened) { self.open(); } else { self.cancelClosing(); } }); Util.observe(box, "mouseout", function (ev) { if (!self.opened || self.pinned) return; if (box != ev.target && box != ev.srcElement) return; self.closeLater(); }); document.body.appendChild(box); } else { var lists = box.getElementsByTagName("ul"); for (var i = 0, len = lists.length; i < len; i++) { var ul = lists[i]; if (ul && /\bplaylist-list-inner\b/.test(ul.className)) { listScrollTop = ul.scrollTop; break; } } box.innerHTML = ""; } this.container = box; /** var header = document.createElement("div"); header.className = "playlist-header"; box.appendChild(header); this.__addLinkButton(header, { caption: "×", title: "プレイリストを閉じる", className: "playlist-window-button playlist-window-button-close", click: function () { self.close(); } }); var title = document.createElement("p"); title.className = "playlist-title"; header.appendChild(title); */ var buttons = document.createElement("div"); buttons.className = "playlist-buttons"; box.appendChild(buttons); this.__addButton(buttons, { caption: "visited On", className: "playlist-button-add-this-video", click: function () { self.site.judgeCSSChange = self.site.changeOn; self.site.save(); //この呼び出し方はまた組みなおす //つもりなので、メソッド化していない。 self.site.getLinks( ); self.site.checkVisited( ); self.site.view( ); } }); this.__addButton(buttons, { caption: "visited Off", className: "playlist-button-add-videos-in-page", click: function () { self.site.judgeCSSChange = self.site.changeOff; self.site.save(); self.site.getLinks( ); self.site.checkVisited( ); self.site.view( ); } }); this.__addButton(buttons, { caption: "OFF", className: "playlist-button-play", click: function () { self.site.judgeCSSChange = self.site.changeAllOff; self.site.save(); self.site.getLinks( ); self.site.checkVisited( ); self.site.view( ); }, }); }, __addLinkButton: function (parent, def) { var btn = document.createElement("a"); btn.href = "javascript:void(0);"; btn.innerHTML = def.caption; btn.className = "playlist-link-button"; if (def.id) btn.id = def.id; if (def.className) btn.className += " " + def.className; if (def.title) btn.title = def.title; if (def.click) Util.observe(btn, "click", (function (b, f) { return function () { f.apply(b, arguments); }; })(btn, def.click)); parent.appendChild(btn); }, __addButton: function (parent, def) { var btn = document.createElement("input"); btn.type = "button"; btn.value = def.caption; btn.className = "submit playlist-button"; if (def.id) btn.id = def.id; if (def.className) btn.className += " " + def.className; if (def.disabled) btn.disabled = true; if (def.click) Util.observe(btn, "click", (function (b, f) { return function () { f.apply(b, arguments); }; })(btn, def.click)); parent.appendChild(btn); }, }; var Util = { observe: function (elem, event, func, capture) { if (elem.attachEvent) { elem.attachEvent("on" + event, func); } else if (elem.addEventListener) { elem.addEventListener(event, func, !!capture); } else { elem["on" + event] = func; } }, stopObserving: function (elem, event, func, capture) { if (elem.detachEvent) { elem.detachEvent("on" + event, func); } else if (elem.removeEventListener) { elem.removeEventListener(event, func, !!capture); } else { delete elem["on" + event]; } } }; //余計なスタイルはいってるけど、そこはスルーで GM_addStyle(<><![CDATA[ .playlist-controller { position: fixed; width: 400px; left: 0px; top: 30px; border: 1px solid #CCC; background-color: #FFF; overflow: hidden; } .playlist-controller.closed { position: fixed; left: -395px; } .playlist-header { margin: 5px; padding: 2px 5px; border-bottom: 1px solid #333; } .playlist-title { font-size: 90%; font-weight: bold; } a.playlist-window-button { float: right; display: block; width: 16px; height: 16px; font-size: 12px; font-family: monospace; font-weight: bold; line-height: 16px; text-align: center; text-decoration: none; border-width: 1px; border-style: solid; border-color: #CCC #999 #999 #CCC; margin-right: 2px; outline: none; } a.playlist-window-button-pin.pinned { border-color: #999 #CCC #CCC #999; background-color: #AAA; } .playlist-buttons { margin: 10px; position: relative; } .playlist-buttons:after { content: "."; clear: both; display: block; height: 0; visibility: hidden; } .playlist-button { margin-right: 5px; float: left; } .playlist-button-clear { float: right; } .playlist-list-outer { margin: 0 10px; padding: 0; border: 1px solid #999; height: 205px; } .playlist-list-inner { list-style-type: none; margin: 0; padding: 0; width: 100%; height: 100%; overflow: auto; } .playlist-list-item { white-space: nowrap; } .playlist-list-item.even { background-color: #EEE; } a.playlist-list-item-title { display: block; padding: 2px 5px; overflow: hidden; outline: none; } a.playlist-list-item-button { font-size: 12px; font-family: monospace; font-weight: bold; text-decoration: none; padding: 2px 5px; float: right; outline: none; } a.playlist-list-item-button-del { color: #D00; } .playlist-footer { margin: 5px 10px; text-align: right; } .playlist-footer:after { content: "."; clear: both; display: block; height: 0; visibility: hidden; } .playlist-mode-selector { float: left; } a.playlist-mode-finish { float: left; font-size: 12px; text-decoration: none; padding: 0 5px; border-width: 1px; border-style: solid; border-color: #CCC #999 #999 #CCC; outline: none; margin-left: 5px; } .playlist-checkbox { margin-left: 10px; vertical-align: middle; } .playlist-checkbox-label { vertical-align: middle; } ]]></>); // ------------------------------------------------------------------ console.log( 'load: my script'); new PageController();
う〜ん。微妙にレイアウト崩れる。