カテゴリー 「Greasemonkey」 RSS

2008-12-26

[]Hatena::Bookmark::24H::Chart

修正:いい加減&が変換されるのを何とかしてほしい

解説:Hatena::Bookmark::24H(http://hatebu24h.ashitano.in/)に、トップエントリの獲得したブックマーク数の推移のチャートを加えます。

// ==UserScript==
// @name           chart of Hatena::Bookmark::24H
// @namespace      http://anond.hatelabo.jp/
// @include        http://hatebu24h.ashitano.in/*
// ==/UserScript==

var url = unescape("http://chart.apis.google.com/chart?chs=160x60%26cht=ls%26chd=t:");
url = url + $X("//div[@class='clocktxt']", Array).map(function(s){return s.firstChild.nodeValue}).join(",");
//var id = $X("//h3/a/@href")[0].nodeValue;
//url = url + $X("//div[@class='entrytitle' or @class='entrytitle2'][.//a[@href='"+id+"']]/../preceding-sibling::div[1]", Array).map(function(s){return s.textContent.match(/\d+/)}).join(",");
var before = makeElements({
    nodeName: "div",
    className: "sidebox",
    childNodes: [{
        nodeName: "div",
        className: "sidetitle",
        innerHTML: "Recent top entry chart"
      },{
        nodeName: "div",
        className: "sidetitle",
        childNodes: {
            nodeName: "img",
            src: url
        }
    }]
});
var after = $X("//div[@class='sidebox']", Array)[0];
after.parentNode.insertBefore(before, after);

// util

// var 0.01
function makeElements(obj) {
    if (typeof obj != "object")
        return document.createTextNode(obj);
    if (obj instanceof Array)
        return obj.map(makeElements);
    var node = document.createElement(obj.nodeName);
    delete obj.nodeName;
    if (obj.childNodes) {
        [].concat(makeElements(obj.childNodes)).forEach(node.appendChild, node);
        delete obj.childNodes;
    }
    function extend(dst, src) {
        for (var i in src) {
            if (typeof src[i] == "object" && dst[i] && typeof dst[i] == "object")
                extend(dst[i], src[i]);
            else
                node[i]=obj[i];
        }
    }
    extend(node, obj);
    return node;
}

// by http://lowreal.net/blog/2007/11/17/1
// $X(exp);
// $X(exp, context);
// $X(exp, type);
// $X(exp, context, type);
function $X (exp, context, type /* want type */) {
    if (typeof context == "function") {
        type    = context;
        context = null;
    }
    if (!context) context = document;
    var exp = (context.ownerDocument || context).createExpression(exp, function (prefix) {
        var o = document.createNSResolver(context).lookupNamespaceURI(prefix);
        if (o) return o;
        return (document.contentType == "application/xhtml+xml") ? "http://www.w3.org/1999/xhtml" : "";
    });

    switch (type) {
        case String:
            return exp.evaluate(
                context,
                XPathResult.STRING_TYPE,
                null
            ).stringValue;
        case Number:
            return exp.evaluate(
                context,
                XPathResult.NUMBER_TYPE,
                null
            ).numberValue;
        case Boolean:
            return exp.evaluate(
                context,
                XPathResult.BOOLEAN_TYPE,
                null
            ).booleanValue;
        case Array:
            var result = exp.evaluate(
                context,
                XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
                null
            );
            var ret = [];
            for (var i = 0, len = result.snapshotLength; i < len; i++) {
                ret.push(result.snapshotItem(i));
            }
            return ret;
        case undefined:
            var result = exp.evaluate(context, XPathResult.ANY_TYPE, null);
            switch (result.resultType) {
                case XPathResult.STRING_TYPE : return result.stringValue;
                case XPathResult.NUMBER_TYPE : return result.numberValue;
                case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
                case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: {
                    // not ensure the order.
                    var ret = [];
                    var i = null;
                    while (i = result.iterateNext()) {
                        ret.push(i);
                    }
                    return ret;
                }
            }
            return null;
        default:
            throw(TypeError("$X: specified type is not valid type."));
    }
}

2008-07-22

[]関連エントリーのツリーをたどれるグレモン

結局概要表示機能追加した。unsafeWindow使いまくり大丈夫かいな。

// ==UserScript==
// @name	Hatena Bookmark Tree Expander
// @namespace	http://anond.hatelabo.jp/
// @include	http://b.hatena.ne.jp/entry/*
// ==/UserScript==
// <div class="info">
//   <ul id="similar_entries" class="bookmarklist">
//     <li></li>
//   </ul>
// </div>
// <div class="info">
//   <ul id="referred_entries" class="bookmarklist">
//     <li id="referred-entry-\d+"></li>
//   </ul>
// </div>
// <div class="info">
//   <ul id="relation_diary" class="bookmarklist">
//     <li id="diary-{id}-\d+"></li>
//   </ul>
// </div>
(function() {

function main() {
	loadBookmarkCommentViewer();
	similar.prototype.rootAppend();
	referred.prototype.rootAppend();
}

function HBTM(target) {
	this.target = target;
	this.targetXPath = "//ul[@id='"+target+"']/li";
	this.targetRegExp = new RegExp('<ul id="'+target+'"(.|\\s)*?</ul>');
}
HBTM.prototype = {
	openIcon:
		'<img width="15" height="15" class="icon" style="opacity: 0.6" src="http://anond.hatelabo.jp/images/common/open.gif"/>',
	closeIcon:
		'<img width="15" height="15" class="icon" style="opacity: 0.6" src="http://anond.hatelabo.jp/images/common/close.gif"/>',
	loadingIcon:
		'<img width="13" height="13" class="icon" src="http://anond.hatelabo.jp/images/common/loading.gif"/>',
	commentIcon: function(url) {
		return '<img class="hatena-bcomment-view-icon" src="http://r.hatena.ne.jp/images/popup.gif" onclick="iconImageClickHandler(this, \''+url+'\', event);">'
	},
	create: function() {
		this.comment = document.createElement("span");
		this.comment.innerHTML = this.commentIcon($X("string(descendant::a/@href)", this.node).value());
		this.open = document.createElement("a");
		this.open.href = "javascript:void(0)";
		this.open.innerHTML = this.openIcon;
		this.close = document.createElement("a");
		this.close.href = "javascript:void(0)";
		this.close.innerHTML = this.closeIcon;
		this.close.style.display = "none";
		this.loading = document.createElement("span");
		this.loading.innerHTML = this.loadingIcon;
		this.loading.style.display = "none";
		this.node.appendChild(this.comment);
		this.node.appendChild(document.createTextNode(" "));
		this.node.appendChild(this.open);
		this.node.appendChild(this.close);
		this.node.appendChild(this.loading);
		this.open.addEventListener("click", bind(this.openAct, this), false);
		this.close.addEventListener("click", bind(this.closeAct, this), false);
	},
	openAct: function() {
		this.open.style.display = "none";
		if (this.tree) {
			this.tree.style.display = "block";
			this.close.style.display = "inline";
		} else {
			this.loading.style.display = "inline";
			this.load();
		}
	},
	closeAct: function() {
		if (this.tree) {
			this.tree.style.display = "none";
			this.close.style.display = "none";
			this.open.style.display = "inline";
		}
	},
	load: function() {
		var url = $X("string(descendant::a[starts-with(@href, '/entry/')]/@href)", this.node).value();
		GM_xmlhttpRequest({
			method: "GET",
			url: "http://b.hatena.ne.jp"+url,
			onload: bind(this.loadCallback, this)
		});
	},
	loadCallback: function(result) {
		var match = result.responseText.match(this.targetRegExp);
		if (match) {
			var sandbox = document.createElement("div");
			sandbox.innerHTML = match[0].replace(this.target,"");
			this.tree = sandbox.firstChild;
		} else {
			this.tree = document.createElement("ul");
		}
		this.append();
		this.loading.style.display = "none";
		this.close.style.display = "inline";
	},
	append: function() {
		this.tree.style.backgroundColor = "transparent";
		this.tree.style.listStyleType = "circle";
		this.node.appendChild(this.tree);
		$X("li", this.tree).each(function(n) {
			var a = $X("a",n).node();
			var c = $X("count(//li/a[@href='"+a.href+"'])").value();
			if (c > 1) n.parentNode.removeChild(n);
		});
		$X("li", this.tree).each(bind(function(node){new this.constructor(node)}, this));
	},
	rootAppend: function() {
		$X(this.targetXPath).each(bind(function(node){new this.constructor(node)}, this));
	}
};

function similar(node) {
	this.node = node;
	this.create();
}
similar.prototype = new HBTM("similar_entries");
similar.prototype.constructor = similar;

function referred(node) {
	this.node = node;
	this.create();
}
referred.prototype = new HBTM("referred_entries");
referred.prototype.constructor = referred;

function loadBookmarkCommentViewer() {
	var head = document.getElementsByTagName("head")[0];
	var script = document.createElement("script");
	script.type = "text/javascript";
	script.src = "http://b.hatena.ne.jp/js/BookmarkCommentViewerAllInOne.1.2.js";
	head.appendChild(script);
	var css = document.createElement("link");
	css.rel="stylesheet";
	css.href="http://d.hatena.ne.jp/css/base.css";
	css.type="text/css";
	css.media="all";
	head.insertBefore(css, head.firstChild);
	window.addEventListener("load",function(){
		var BCV = unsafeWindow.BookmarkCommentViewer;
		BCV.options['screenshot'] = true;
		var asyncCommnetView = BCV.asyncCommnetView;
		BCV.asyncCommnetView = function(url, onCompleteCallback) {
			var div = asyncCommnetView(url, function(){
				onCompleteCallback.apply(this, arguments);
				new unsafeWindow.Ten.XHR("http://b.hatena.ne.jp/entry/rss/"+url, {}, function(result) {
					if (! result.responseText.match(/<description>(.*?)<\/description>/))
						return;
					if (! RegExp.$1)
						return;
//					var desc = document.createTextNode("desc: "+RegExp.$1);
					var desc = document.createElement("li");
					desc.appendChild(document.createTextNode("desc: "+RegExp.$1));
					div.lastChild.insertBefore(desc,div.lastChild.getElementsByTagName("li")[0]);
				});
			});
			return div;
		};
		BCV.asyncCommnetView.origin = asyncCommnetView;
	}, false);
}

function bind(f,o) {return function() {return f.apply(o, arguments)}}

function $X(xpath, context) {
	if (!(this instanceof $X))
		return new $X(xpath, context);
	this.xpath = xpath;
	this.context = context || document;
}
$X.prototype = {
	evaluate: function() {
		var result = document.evaluate(this.xpath, this.context, null, this.type, null);
		switch (result.resultType) {
			case XPathResult.STRING_TYPE : return result.stringValue;
			case XPathResult.NUMBER_TYPE : return result.numberValue;
			case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
			case XPathResult.FIRST_ORDERED_NODE_TYPE: return result.singleNodeValue;
		}
		return result;
	},
	node: function() {
		this.type = XPathResult.FIRST_ORDERED_NODE_TYPE;
		return this.evaluate();
	},
	value: function() {
		this.type = XPathResult.ANY_TYPE;
		return this.evaluate();
	},
	each: function(func) {
		this.type = XPathResult.ORDERED_NODE_SNAPSHOT_TYPE;
		var result = this.evaluate();
		for (var i=0; i<result.snapshotLength; i++)
			func(result.snapshotItem(i));
	}
};

main();

})();

2008-07-21

[]関連エントリーのツリーをたどれるグレモン

ついでに含むエントリーもたどれるようにして、さらに、コメントビューアも付けた。



書いてみた。一部やっつけ感がある。このくらいになるとライブラリ使いたくなってくるな。

そうそう、概要表示とかも欲しいな、とか思いつつ、どこからデータ持ってくるか分からなかったけど、RSSにあるな。

でも、UIメンドイ。コメントビューアをフックできないかな。誰か作って。

2008-03-14

[][]はてブhotentryにて2chコピペブログや「ネタ」を削除

http://anond.hatelabo.jp/20080302214727

ネタ」がうまくいかない件は、"\u30cd\u30bf"にしたらうまく行った

あと2chコピペサイトを2つ追加

とりあえず、Sleipnir2のSeahorseで確認。

// ==UserScript==
// @name           hatebufilter
// @namespace      hatebufilter
// @description    Hatena bookmark filter
// @include        http://b.hatena.ne.jp/hotentry*
// @include        http://b.hatena.ne.jp/entrylist*
// ==/UserScript==
/*
問題点
いまのところなし

・問題が起こりそうなURL
http://b.hatena.ne.jp/hotentry?mode=daily&amp;date=20080224
http://b.hatena.ne.jp/hotentry?mode=daily&amp;date=20080218
**/

(function(){
	// Hatebu Tag
	var HatebuTagParentNum = 3;

	var filters = [
		// moconico douga
		{"tag": "div", "name": "entry", "pattern": "nicovideo\.jp"},
/*
		// tag of "2ch"
		{"tag": "a", "name": "tag", "pattern": "2ch", "parentNum": HatebuTagParentNum},
		{"tag": "a", "name": "tag", "pattern": "\\*2ch", "parentNum": HatebuTagParentNum},
**/
		// 2ch blogs  
		//  livedoor
		{"tag": "div", "name": "entry",
			"pattern": /http:\/\/blog\.livedoor\.jp\/(insidears|dqnplus)\//},
		{"tag": "div", "name": "entry",
			"pattern": /http:\/\/(guideline|alfalfa|news4vip)\.livedoor\.biz\//},
		//  fc2
		{"tag": "div", "name": "entry",
			"pattern": /http:\/\/(imihu|urasoku|news23vip|waranote|vipvipblogblog|netanabe|res2ch|kanasoku|tenkomo)\.blog\d+\.fc2\.com\//},
		{"tag": "div", "name": "entry",
			"pattern": /http:\/\/www\.kajisoku\.org\//},
		
		// hatena anonymouse diary
		{"tag": "div", "name": "entry", "pattern": /http:\/\/anond\.hatelabo\.jp\//},

		// tag of "neta"
		{"tag": "a", "name": "tag", "pattern": "\u30cd\u30bf", "parentNum": HatebuTagParentNum},
		{"tag": "a", "name": "tag", "pattern": "\\*\u30cd\u30bf", "parentNum": HatebuTagParentNum},
	];

	for (var i = 0; i < filters.length; i++) {
		var f = filters[i];
		filtering(f.tag, f.name, f.pattern, f.parentNum== undefined ? 1 : f.parentNum);
	}

	function filtering(tag, name, pattern, parentNodeNum){
		var entrylist = document.getElementsByTagName(tag);
                //print("pattern = " + pattern);
		for(var idx = entrylist.length - 1; 0 <= idx; idx--){
//        for(var idx = 0; idx < entrylist.length - 1; idx++){
			if (entrylist[idx].className == name){
				if (entrylist[idx].innerHTML.match(pattern)) {
					var node = entrylist[idx];
					var oldNode = null;
					for (var j = 0; j < parentNodeNum; j++) {
						oldNode = node;
						node = node.parentNode;
					}
					// print("class = " + oldNode.getAttribute("class"));
					// print("id = " + oldNode.getAttribute("id"));
					node.removeChild(oldNode);
				}
			}
		}
	}
})();

2008-03-08

[][greasemonkey][seahorse]はてブのhotentryで、2chコピペブログや「ネタ」を削除す...勝手に改造

firefoxでしか確認していないけれど、URL正規表現XPathで指定できる様にしてみたよ。

// ==UserScript==
// @name           filter for Hatena::Bookmark
// @namespace      http://anond.hatelabo.jp/
// @include        http://b.hatena.ne.jp/hotentry*
// @include        http://b.hatena.ne.jp/entrylist*
// origin http://anond.hatelabo.jp/20080302214727
// ==/UserScript==
(function(){
	var itemxpath = "//div[@class='entry']";
	function xpathgenURL(url) {return "//div[@class='entry' and descendant::a[starts-with(@href,'"+url+"')]]"}
	var filters = [
		// start with '//' then xpath
		// moconico douga
//		{"tag": "div", "name": "entry", "pattern": "nicovideo\.jp"},
		"//div[@class='entry' and descendant::a[contains(@href,'nicovideo.jp')]]",
/*
		// tag of "2ch"
		{"tag": "a", "name": "tag", "pattern": "2ch", "parentNum": HatebuTagParentNum},
		{"tag": "a", "name": "tag", "pattern": "\\*2ch", "parentNum": HatebuTagParentNum},
***/
		// start with 'http' then url
		// 2ch blogs  
		//  livedoor
//		{"tag": "div", "name": "entry",
//			"pattern": /http:\/\/blog\.livedoor\.jp\/(insidears|dqnplus)\//},
		"http://blog.livedoor.jp/insidears/",
		"http://blog.livedoor.jp/dqnplus/",
//		{"tag": "div", "name": "entry",
//			"pattern": /http:\/\/(guideline|alfalfa|news4vip)\.livedoor\.biz\//},
		"http://guideline.livedoor.biz/",
		"http://alfalfa.livedoor.biz/",
		"http://news4vip.livedoor.biz/",
		// typeof /regexp/ is function (@firefox) then regexp pattern
		//  fc2
//		{"tag": "div", "name": "entry",
//			"pattern": /http:\/\/(urasoku|news23vip|waranote|vipvipblogblog|netanabe|res2ch|kanasoku|tenkomo)\.blog\d+\.fc2\.com\//},
		/http:\/\/(urasoku|news23vip|waranote|vipvipblogblog|netanabe|res2ch|kanasoku|tenkomo)\.blog\d+\.fc2\.com\//,

		// tag of "neta"
//		{"tag": "a", "name": "tag", "pattern": "ネタ", "parentNum": HatebuTagParentNum},
		"//div[@class='entry' and descendant::a[@class='tag' and string()='ネタ']]",
//		{"tag": "a", "name": "tag", "pattern": "*ネタ", "parentNum": HatebuTagParentNum},
		"//div[@class='entry' and descendant::a[@class='tag' and string()='*ネタ']]",

		// hatena anonymouse diary
//		{"tag": "div", "name": "entry", "pattern": /http:\/\/anond\.hatelabo\.jp\//}
		"http://anond.hatelabo.jp/",
	];

	for (var i=0; i<filters.length; i++) {
		var filter = filters[i];
		var type = typeof filter;
		var regexp;
		var xpath;
		if (type == "function") {
			xpath = itemxpath;
			regexp = filter;
		} else if (type == "string") {
			if (filter.match(/^http/)) {
				xpath = xpathgenURL(filter);
			} else if (filter.match(/^\/\//)) {
				xpath = filter;
			} else {
				next;
			}
		}
		var removeNodes = document.evaluate(xpath,document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null);
		for (var j=0; j<removeNodes.snapshotLength; j++) {
			var node = removeNodes.snapshotItem(j);
			if (!regexp || node.innerHTML.match(regexp)) {
				node.parentNode.removeChild(node);
			}
		}
	}
})();

ついでに増田版も作ってみたよ。

// ==UserScript==
// @name           filter for Hatelabo::AnonymousDiary
// @namespace      http://anond.hatelabo.jp/
// @include        http://anond.hatelabo.jp/
// @include        http://anond.hatelabo.jp/*?page=*
// @exclude        http://anond.hatelabo.jp/YourID/*
// ==/UserScript==
// origin http://anond.hatelabo.jp/20080302214727
(function(){
	var itemxpath = "//div[@class='section']";
	function xpathgenURL(url) {return "//div[@class='section' and descendant::a[starts-with(@href,'"+url+"')]]"}
	var filters = [
		// start with '//' then xpath
		"//div[@class='section' and child::h3[starts-with(string(),'■はてな嫌われ者!')]]",
		// start with 'http' then url
		"http://anond.hatelabo.jp/",
		// typeof /regexp/ is function (@firefox) then regexp pattern
		/釣り/,
	];

	for (var i=0; i<filters.length; i++) {
		var filter = filters[i];
		var type = typeof filter;
		var regexp;
		var xpath;
		if (type == "function") {
			xpath = itemxpath;
			regexp = filter;
		} else if (type == "string") {
			if (filter.match(/^http/)) {
				xpath = xpathgenURL(filter);
			} else if (filter.match(/^\/\//)) {
				xpath = filter;
			} else {
				next;
			}
		}
		var removeNodes = document.evaluate(xpath,document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null);
		for (var j=0; j<removeNodes.snapshotLength; j++) {
			var node = removeNodes.snapshotItem(j);
			if (!regexp || node.innerHTML.match(regexp)) {
				node.parentNode.removeChild(node);
			}
		}
	}
})();

2008-03-02

[][]はてブのhotentryで、2chコピペブログや「ネタ」を削除する

http://anond.hatelabo.jp/20080102122736

汎用性を上げてみた。

はてブのhotentryから削除するgreasemonkey

Sleipnir2のseahorseでも使える。

// ==UserScript==
// @name           hatebufilter
// @namespace      hatebufilter
// @description    Hatena bookmark filter
// @include        http://b.hatena.ne.jp/hotentry*
// @include        http://b.hatena.ne.jp/entrylist*
// ==/UserScript==
/*
問題点
いまのところなし

・問題が起こりそうなURL
http://b.hatena.ne.jp/hotentry?mode=daily&amp;date=20080224
http://b.hatena.ne.jp/hotentry?mode=daily&amp;date=20080218
**/

(function(){
	// Hatebu Tag
	var HatebuTagParentNum = 3;

	var filters = [
		// moconico douga
		{"tag": "div", "name": "entry", "pattern": "nicovideo\.jp"},
/*
		// tag of "2ch"
		{"tag": "a", "name": "tag", "pattern": "2ch", "parentNum": HatebuTagParentNum},
		{"tag": "a", "name": "tag", "pattern": "\\*2ch", "parentNum": HatebuTagParentNum},
**/
		// 2ch blogs  
		//  livedoor
		{"tag": "div", "name": "entry",
			"pattern": /http:\/\/blog\.livedoor\.jp\/(insidears|dqnplus)\//},
		{"tag": "div", "name": "entry",
			"pattern": /http:\/\/(guideline|alfalfa|news4vip)\.livedoor\.biz\//},
		//  fc2
		{"tag": "div", "name": "entry",
			"pattern": /http:\/\/(urasoku|news23vip|waranote|vipvipblogblog|netanabe|res2ch|kanasoku|tenkomo)\.blog\d+\.fc2\.com\//},

		// tag of "neta"
		{"tag": "a", "name": "tag", "pattern": "ネタ", "parentNum": HatebuTagParentNum},
		{"tag": "a", "name": "tag", "pattern": "*ネタ", "parentNum": HatebuTagParentNum},

		// hatena anonymouse diary
		{"tag": "div", "name": "entry", "pattern": /http:\/\/anond\.hatelabo\.jp\//}
	];

	for (var i = 0; i < filters.length; i++) {
		var f = filters[i];
		filtering(f.tag, f.name, f.pattern, f.parentNum== undefined ? 1 : f.parentNum);
	}

	function filtering(tag, name, pattern, parentNodeNum){
		var entrylist = document.getElementsByTagName(tag);
                //print("pattern = " + pattern);
		for(var idx = entrylist.length - 1; 0 <= idx; idx--){
//        for(var idx = 0; idx < entrylist.length - 1; idx++){
			if (entrylist[idx].className == name){
				if (entrylist[idx].innerHTML.match(pattern)) {
					var node = entrylist[idx];
					var oldNode = null;
					for (var j = 0; j < parentNodeNum; j++) {
						oldNode = node;
						node = node.parentNode;
					}
					// print("class = " + oldNode.getAttribute("class"));
					// print("id = " + oldNode.getAttribute("id"));
					node.removeChild(oldNode);
				}
			}
		}
	}
})();

hatebufilter.user.jsなどとUTF-8で保存して使う。

しかし、増田コード記法日本語貼り付けたら化けるんだが・・・どうすればいいんだろ?

コメントアウトを直せば、2chコピペブログ以外の「2chタグ自体での削除も可能です。

いろいろ削除していると、まーオレンジニュースでいいじゃんという。

# スーパー引用記法にしました

2007-10-07

[]増田リーダーで投稿できるようになった件

こげんデモとね
→ Flash。

詳細に関しては、本記事を。

なんか、もはや日記じゃなくなったな、これ。

2007-10-01

[]msd_reader

http://anond.hatelabo.jp/20070930223945

下ビューの選択中に起きる不具合を修正。

「ん、なんかリンクずれてない?」って現象を。

 
ログイン ユーザー登録
ようこそ ゲスト さん