ReblogCommand を LDR 上で使ったときにピンを吹っ飛ばさないようにする

ReblogCommand は便利なのだけど LDR 上で使った場合に
reblog できないピンを全部吹っ飛ばすという困ったところがある。
例えば、普通に LDR を使っていてピンを20本立てていたとする。
この状態で reblog をしようとするか、または過って「t」を押すと
立てていたピン20本の情報が丸ごと全部失われる。
ピンの数がけっこう多いときにこれをやらかすとショックが大きい…(´・ω・`)


ReblogCommand 用 Patch - 枕を欹てて聴くにて
id:Constellation さんがこれを防ぐためのパッチを紹介されている。
でも、このパッチはちょっと古いバージョン用に書かれたものである。
そこで、僕が使っている最新版の Revison 13668 (2008/07/14 現在) に
このパッチの機能を取り込んでみた。
また、そのついでにソースの気になる部分を自分なりに書き直してみたりした。


変更箇所をざっとまとめるとこんな感じ。

機能的な変更

  • ピンに reblog できるエントリがある場合は「t」でまとめてポストできるようにした
  • ピンに reblog できないエントリがある場合は吹っ飛ばさずにそのまま残るようにした

機能的には id:Constellation のパッチを最新版にあてた感じになっているはず。

ソースコード的な変更

  • パッチに書かれていた if 文で条件が分かりにくいところがあったので書き直した。
  • location.href が fastladder のものか LDR のものかを判断させる箇所がいくつかあったので関数にした。
  • インデントのスタイルはハードタブだと思われるが、微妙にソフトタブが混じっていたりしたのでハードタブに統一した。
  • if や else の書き方が窮屈そうで、個人的にあまり好きじゃないので書き直した。
// こういう書き方だったのを
if(condition){
  //...
}else{
  //...
}
// こう書き直した
if (condition) {
  //...
} else {
  //...
}
  • function などの後にくる { の前にも空白を1つ入れるようにした。

書き直したスクリプト

CodeRepos にコミットしようかとも思ったんだけど
コーディングスタイルなどをいじってしまったこととかが気になるので
とりあえずスクリプトをそのまま張り付けておくに留める。

追記

やっぱり CodeRepos にコミットした。
「コーディングスタイルとかよりもスクリプトが分散する方がよくない」ということで。
http://coderepos.org/share/browser/lang/javascript/userscripts/reblogcommand.user.js?

// ==UserScript==
// @name           ReblogCommand
// @namespace      http://white.s151.xrea.com/
// @include        http://*
// @include        https://*
// @exclude        http://www.tumblr.com/share
//
// patched by Constellation and gan2
//
// ==/UserScript==

(function(){

	const ALLOW_OWN_DOMAIN = true;

	if (!window.Minibuffer) return;
	var $X = window.Minibuffer.$X;
	var D  = window.Minibuffer.D;

// ----------------------------------------------------------------------------
// Reblog
// ----------------------------------------------------------------------------

	function isTumblrDashboardURL(url) {
		return url.match("^http://www\\.tumblr\\.com/dashboard.*$") ? true : false;
	}

	function isTumblrUserURL(url) {
		return url.match("^https?://[^.]+\\.tumblr\\.com/post/(\\d+)/?.*$") ||
			// tumblr allow to use own domain. but this is risky.
			(ALLOW_OWN_DOMAIN && url.match("^https?://[^/]+/post/(\\d+)/?.*$")) ? true : false;
	}

	function isFdrOrLdrURL(url) {
		return (url == 'http://fastladder.com/reader/' ||
		        url == 'http://reader.livedoor.com/reader/') ? true : false;
	}

	function getIDByPermalink(url) {
		if (isTumblrUserURL(url)) {
			return RegExp.$1;
		} else {
			// return what ?
			return false;
		}
	}

	function getURLByID(id, token) {
		if (token) return "http://www.tumblr.com/reblog/" + id + "/" + token + "?redirect_to=/dashboard";
		return "http://www.tumblr.com/reblog/" + id;
	}

	// copy from tombloo
	function unescapeHTML(s) {
		return s.replace(/"/g, '"')
		        .replace(/&lt;/g, '<')
		        .replace(/&gt;/g, '>')
		        .replace(/&amp;/g, '&');
}

	// copy from tombloo
	function getReblogToken(url) {
		url = unescapeHTML(url);
		if (/&pid=([^&]*)&rk=([^&\"]*)/.test(url) || /\/reblog\/(.*?)\/([^?]*)/.test(url))
			return {
				id    : RegExp.$1,
				token : RegExp.$2
			};
	}

	function convertToHTMLDocument(html) {
		var xsl = (new DOMParser()).parseFromString(
			'<?xml version="1.0"?>\
				<stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform">\
				<output method="html"/>\
			</stylesheet>', "text/xml");
		var xsltp = new XSLTProcessor();
		xsltp.importStylesheet(xsl);
		var doc = xsltp.transformToDocument(document.implementation.createDocument("", "", null));
		doc.appendChild(doc.createElement("html"));
		var range = doc.createRange();
		range.selectNodeContents(doc.documentElement);
		doc.documentElement.appendChild(range.createContextualFragment(html));
		return doc;
	}

	function parseParams(doc) {
		var elms = $X('id("edit_post")//*[name()="INPUT" or name()="TEXTAREA" or name()="SELECT"]', doc);
		var params = {};
		elms.forEach(function(elm) {
			params[elm.name] = elm.value;
		});
		return params;
	}

	function createPostData(params) {
		var arr = [];
		for(var param in params) {
			if(param != "preview_post") {
				arr.push(encodeURIComponent(param));
				arr.push("=");
				arr.push(encodeURIComponent(params[param]));
				arr.push("&");
			}
		}
		return arr.join('');
	}

	function reblog(aURL) {
		var id  = getIDByPermalink(aURL);
		var d;
		with (D()) {
			d = Deferred();
			if (!id) {
				wait(0).next(function() { d.call(); });
				return d;
			}
		}
		window.Minibuffer.status('ReblogCommand'+id, 'Reblog ...');
		d = D();
		var url = aURL;
		d.xhttp.get(url).
		next(function(res) {
			var token = getReblogToken(res.responseText.match('iframe src="((?:\\"|[^"])*)"')[1]);
			url = getURLByID(token.id, token.token);
			return d.xhttp.get(url);
		}).
		next(function(res) {
			return d.xhttp.post(url, createPostData( parseParams( convertToHTMLDocument(res.responseText))));
		}).
		next(function() { window.Minibuffer.status('ReblogCommand'+id, 'Reblog ... done.', 100); d.call()}).
		error(function() {
			if (confirm('reblog manually ? \n' + url)) reblogManually(url);
			d.call();
		});
		return d;
	}

	function reblogManually(aURL) {
		var url = aURL;
		var d = D();
		d.xhttp.get(url).
		next(function(res) {
			var token = getReblogToken(res.responseText.match('iframe src="((?:\\"|[^"])*)"')[1]);
			url = getURLByID(token.id, token.token);
			window.open(url);
		});
	}

	function spClearPins(url) {
		unsafeWindow.pin.remove(url);
	}

// ----------------------------------------------------------------------------
// Command
// ----------------------------------------------------------------------------


	function getTargetCommand() {
		var target_cmd = '';
		if (isFdrOrLdrURL(window.location.href)) {
			target_cmd = 'pinned-or-current-link';
		} else if (isTumblrUserURL(window.location.href)) {
			target_cmd = 'location';
		} else if (window.LDRize) {
			target_cmd = 'pinned-or-current-link';
		} else {
			target_cmd = 'location';
		}
		return target_cmd;
	}

	window.Minibuffer.addShortcutkey({
		key: 't',
		description: 'Reblog',
		command: function() {
			var target_cmd = getTargetCommand();
			var clear_pin  = '';
			if (isFdrOrLdrURL(window.location.href)) {
				if (!window.Minibuffer.execute('pinned-or-current-link').filter(isTumblrUserURL).length) {
					target_cmd = 'current-link';
					clear_pin = '';
				} else {
					clear_pin = '-c';
				}
			} else {
				clear_pin = (target_cmd == 'pinned-or-current-link') ? ' | clear_pin' : '';
			}
			window.Minibuffer.execute(target_cmd + ' | reblog ' + clear_pin);
		}});

	window.Minibuffer.addShortcutkey({
		key: 'T',
		description: 'Reblog manually',
		command: function() {
			var target_cmd = getTargetCommand();
			var clear_pin = (target_cmd == 'pinned-or-current-link') ? ' | clear-pin' : '';
			window.Minibuffer.execute(target_cmd + ' | reblog -m' + clear_pin );
		}});

	window.Minibuffer.addCommand({
		name: 'reblog',
		command: function(stdin) {
			var args = this.args;
			var urls = [];
			if(!stdin.length) {
				if(isTumblrDashboardURL(window.location.href.toString())) {
					var link = window.Minibuffer.execute('current-link');
					if (link) urls = [link.toString()];
				} else {
					// command line is just 'reblog'
					urls = [window.location.href];
				}
			} else if (isFdrOrLdrURL(window.location.href)) {
				var tmp = [];
				stdin.forEach(function(url, index) { tmp[index] = url; });
				urls = tmp;
			} else if (stdin.every(function(a) { return typeof a == 'string' })) {
				// command line is 'location | reblog'
				urls = stdin;
			} else if (stdin.every(function(a) { return a && a.nodeName == 'A'; })) {
				// command line is 'pinned-or-current-link | reblog'
				urls = stdin.map(function(node) { return node.href; });
			}
			
			// reblog
			if (args.length == 1 && args[0] == '-m') {
				urls.forEach(reblogManually);
			} else {
				urls = urls.filter(isTumblrUserURL);
				if (isFdrOrLdrURL(window.location.href) && args.length == 1 && args[0] == '-c') {
					urls.forEach(spClearPins);
				}
				if (!urls.length) return stdin;
				var lst = urls.map(reblog);
				if (lst.length > 1) {
					with (D()) {
						parallel(lst).wait(2).
						next(function() { window.Minibuffer.status('ReblogCommand','Everything is OK', 1000); });
					}
				}
			}
			return stdin;
		}
	});

})();


あ、あれこういうときはパッチ作った方が分かりやすいのかな。まあいいか。