TumblrTagCommand

By whym Last update Feb 8, 2010 — Installed 3,066 times.

There are 12 previous versions of this script.

// ==UserScript==
// @name           TumblrTagCommand
// @namespace      http://whym.github.com
// @description    Minibuffer command for tagging Tumblr posts. Type 'p' to mark/unmark a post and type 'g' to tag. Requires Minibuffer, LDRize & Autopagerize.
// @include        http://www.tumblr.com/dashboard*
// @include        http://www.tumblr.com/show/*
// @include        http://www.tumblr.com/drafts*
// @include        http://www.tumblr.com/queue*
// @include        http://www.tumblr.com/tumblelog/*
// @include        http://*.tumblr.com/tagged/*
// @include        http://*.tumblr.com/post/*
// @include        http://*.tumblr.com/private/*
// @include        http://*.tumblr.com/search/*
// @include        http://*.tumblr.com/page/*
// @include        http://*.tumblr.com/archive/*
// @include        http://*.tumblr.com/
// @version        0.0.6
// ==/UserScript==

// most parts are copied from ReblogCommand
// http://coderepos.org/share/browser/lang/javascript/userscripts/reblogcommand.user.js

(function() {

	const KEY_TAG       = 'g';
	const KEY_TAG_ALL   = 'G';
	const COMMAND_TAG   = 'tumblr-tag-add';
	const COMMAND_POSTS = 'tumblr-posts';
	const ALLOW_OWN_DOMAIN = true;
	const REGEXP_SEPARATOR = /,/;
	const SEPARATOR = ',';

var boot = function() {
	var $X = window.Minibuffer.$X;
	var D  = window.Minibuffer.D;
	var createDocumentFromString  = window.Minibuffer.createDocumentFromString;

// ----------------------------------------------------------------------------
// Tumblr Tag
// ----------------------------------------------------------------------------

	function parseTagList(args) {
		var ret = [];
		if ( !(args instanceof Array) ) {
			args = [args];
		}
		args.forEach(function(x){
			x = (''+x).trim();
			if (x.length > 0) {
				ret = ret.concat(x.split(REGEXP_SEPARATOR).map(function(x){return x.trim();}));
			}
		});
		return ret;
	}
			  
	Array.prototype.uniq = function() {
		var ret = [];
		var result = this.reduce(function(res, item) {
			if (!res.found[item]) {
				res.values.push(item);
				res.found[item] = 1;
				ret.push(item);
			};
			return res;
		}, { values: [], found: {}});
		return ret;
	};
	Array.prototype.remove = function(x) {
		var removed = {};
		if ( x instanceof Array ) {
			x.forEach(function(x){
				removed[x] = 1;
			});
		} else {
			removed[x] = 1;
		}
		return this.filter(function(item) {
			return !removed[item];
		});
	};
	if ( String.prototype.trim == undefined ) {
		String.prototype.trim = function() {
			return this.replace(/^\s+|\s+$/, '');
		};
	}

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

	function isTumblrUserURL(url) {
		return url.match("^https?://[^.]+\\.tumblr\\.com/(post|private)/(\\d+)") ||
			// tumblr allow to use own domain. but this is risky.
			// $X('id("tumblr_controls")[self::iframe]', Boolean)
			(ALLOW_OWN_DOMAIN && url.match("^https?://[^/]+/(post|private)/(\\d+)")) ? true : false;
	}
	
	function getIDByPermalink(url) {
		if (isTumblrUserURL(url)) {
			return RegExp.$2;
		}
		// return what ?
		return false;
	}

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

	// 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 parseParams(doc) {
		// name() returns in lower case on fx3.6
		var elms = $X('id("edit_post")//*[translate(name(),"input", "INPUT") ="INPUT" or translate(name(),"textarea", "TEXTAREA")="TEXTAREA" or translate(name(),"select", "SELECT")="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" && param != "send_to_twitter") {
				arr.push(encodeURIComponent(param));
				arr.push("=");
				arr.push(encodeURIComponent(params[param]));
				arr.push("&");
			}
		}
		return arr.join('');
	}

	function edittags(aURL, appends, removes) {
		var id = getIDByPermalink(aURL);
		var d;
		with (D()) {
			d = Deferred();
			if (!id) {
				wait(0).next(function() { d.call(); });
				return d;
			}
		}
		var message = '';
		if (appends.length > 0) {
			message += 'Adding '+appends.join(SEPARATOR);
		}
		if (removes.length > 0) {
			message += (message.length > 0 ? ', removing ': 'Removing ')+removes.join(SEPARATOR);
		}
		window.Minibuffer.status(COMMAND_TAG+id, message+' ...');
		d = D();
		var url = aURL;
		var editd = 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) {
			var params = parseParams(createDocumentFromString(res.responseText));
			params['post[tags]'] = parseTagList(params['post[tags]']).concat(appends).remove(removes).uniq().join(SEPARATOR);
			//if(!confirm(params['post[tags]'])){return d;}
			if (!params.form_key)
				editd.fail("invalid params");
			return d.xhttp.post(url, createPostData( params ));
		}).
		next(function(res) {
			if (res.status == 200) {
				if ( ! res.finalUrl.match( /^http:\/\/www\.tumblr\.com\/dashboard/ ) )
					editd.fail("unexpected response: " + res.finalUrl);
				else
					window.Minibuffer.status(COMMAND_TAG+id, message+' ... done.', 100);
			} else {
				window.Minibuffer.status(COMMAND_TAG+id, message+' ... error '+res.status+' .', 100);
				editd.fail(res.status + 'in adding tag');
			}
			d.call();
		}).
		error(function(res) {
			window.Minibuffer.status(COMMAND_TAG+id, message+' ... error.', 100);
			if (confirm('error: '+res+', edit manually ? \n' + url))
				editTagManually(url);
			d.call();
		});
		return d;
	}

	function editTagManually(aURL) {
		var url = aURL;
		if (url.match(/\/edit\//)) {
			GM_openInTab(url);
			return;
		}
		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);
			GM_openInTab(url);
		});
	}


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

	function getTargetCommand() {
		var target_cmd = '';
		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: KEY_TAG,
		description: 'Tag Tumblr posts',
		command: function() {
			var target_cmd = getTargetCommand();
			var clear_pin  = (target_cmd == 'pinned-or-current-link') ? ' | clear-pin' : '';
			var tags = prompt("Input new tags and the tags to be deleted.\n(ex. To add 'picture' and remove 'pict', use 'picture,-pict'.)");
			window.Minibuffer.execute(target_cmd + ' | '+[COMMAND_TAG, tags].join(' ')+clear_pin);
		}
	});
	
	window.Minibuffer.addShortcutkey({
		key: KEY_TAG_ALL,
		description: 'Tag all Tumblr posts in screen',
		command: function() {
			var target_cmd = 'tumblr-posts';
			var tags = prompt("Input new tags and the tags to be deleted.\n(ex. To add 'picture' and remove 'pict', use 'picture,-pict'.)");
			window.Minibuffer.execute(target_cmd + ' | '+[COMMAND_TAG, tags].join(' '));
		}
	});

	window.Minibuffer.addCommand({
		name: COMMAND_POSTS,
		command: function(stdin) {
			var linkx = window.LDRize.getSiteinfo().link;
			var parax = window.LDRize.getSiteinfo().paragraph;
			var getfirst = function(x){
				return x[0] != undefined ? x[0] : x;
			};
			var items = $X(parax).map(function(i){ return getfirst($X(linkx, i));});
			return items;
		}
	});


	window.Minibuffer.addCommand({
		name: COMMAND_TAG,
		command: function(stdin) {
			var args = parseTagList(this.args.join(' '));
			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 'tumblr-tag-add'
					urls = [window.location.href];
				}
			} else {
				urls = [];
				stdin.forEach(function(a){
					if (a && typeof a == 'string') {
						// command line is 'location | tumblr-tag-add'
						urls.push(a);
					} else if (a && a.nodeName && a.href && a.nodeName == 'A') {
						// command line is 'pinned-or-current-link | tumblr-tag-add'
						urls.push(a.href);
					} else {
						window.Minibuffer.status(COMMAND_TAG, 'Unsupported input: ' + a, 1000);
					}
				});
			}
			
			if (urls.length == 0){
				window.Minibuffer.status(COMMAND_TAG, 'no input ', 1000);
				return null;
			}
			
			// edit tags
			if (args.length >= 1) {
				var removes = [];
				var appends = [];
				args.forEach(function(x){
					if (x.charAt(0) == '-') {
						removes.push(x.substr(1));
					} else {
						if (x.charAt(0) == '+')
							x = x.substr(1);
						appends.push(x);
					}
				});
				appends = parseTagList(appends).uniq();
				removes = parseTagList(removes).uniq();
				urls = urls.filter(isTumblrUserURL);
				if (!urls.length) return stdin;
				var lst = urls.map(function(x){return edittags(x, appends, removes);});
				if (lst.length > 1) {
					with (D()) {
						parallel(lst).wait(2).
						next(function() { window.Minibuffer.status(COMMAND_TAG, 'Everything is OK', 1000); });
					}
				}
				return stdin;
			} else {
				window.Minibuffer.status(COMMAND_TAG, 'No tag is specified', 1000);
				return null;
			}
		}
	});
};

	if (document.body.id == 'tinymce')
		return;

	if (window.Minibuffer) {
		boot();
	} else {
		window.addEventListener('GM_MinibufferLoaded', boot, false);
	}

})();