Inline Thumbnail

Expand to view thumbnails in the row.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name Inline Thumbnail
// @namespace inline_thumbnail
// @description Expand to view thumbnails in the row.
// @run-at document-end
// @include http://hootsuite.com/*
// @include https://hootsuite.com/*
// @include http://twitter.com/*
// @include https://twitter.com/*
// @include https://mobile.twitter.com/*
// @include http://www.crowy.net/*
// @include http://twipple.jp/*
// @include https://tweetdeck.twitter.com/*
// @version 1.7.3
// ==/UserScript==

(function() {
function source() { // source code

var VERSION = '1.7.3';
var USE_LOCAL_STORAGE_ENABLE = true;
var MAX_URL_CACHE = 400;

function isSupportXhr2() { return 'withCredentials' in $.ajaxSettings.xhr(); }
function ajax(arg) {
	var done = 0;
	var option = {
		url: arg.url, data: arg.data, dataType: arg.dataType, type: 'GET', cache: true,
		success: function(data, status, xhr) { done++ || arg.success(data, status, xhr); },
		error: function(xhr, status, errorThrown) { done++ || arg.error(xhr, status, errorThrown); },
	};
	if (arg.dataType == 'jsonp') {
		setTimeout(function() { option.error(); }, 15 * 1000);
	}
	$.ajax(option);
}
function loadJQuery() {
	var id = 'dy-load-jq';
	if (document.getElementById(id)) {
		return;
	}
	var script = document.createElement('script');
	script.id = id;
	script.type = 'text/javascript';
	script.src = '//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
	document.body.appendChild(script);
}

function main(EXT_SERV_HOST_URL) { // main logic

var STORE_PREFIX = 'inlineThumbnail.';
var EXPANDED_URL_CACHE_KEY = STORE_PREFIX + 'expandedUrls';
var THUMBNAIL_URL_CACHE_KEY = STORE_PREFIX + 'thumbnailUrls';
var EXT_SERV_ENDS = (function(base) {
	var ret = {
		expandurl: 'expandurl',
		amazon: 'amazon',
		flickr: 'flickr',
		nicovideo: 'nicovideo',
		videosurf: 'videosurf',
		tinami: 'tinami',
		ustream: 'ustream',
		tumblr: 'tumblr',
		rakuten: 'rakuten',
		ogmedia: 'ogmedia',
		myspacevideo: 'myspacevideo',
		pictwitter: 'pictwitter',
		itunes: 'itunes',
		loctouch: 'loctouch',
		i500px: '500px',
		facebook: 'facebook',
		twil: 'proxy/twil'
	};
	$.each(ret, function(key, val) { ret[key] = base + '/' + val; });
	return ret;
})(EXT_SERV_HOST_URL);

var HTTPS_SUPPORT_SITE_REGEX = (function() {
	var fragment = (
		'pbs.twimg.com twitpic.com img.ly yfrog.com p.twipple.jp farm\\d.static.flickr.com miil.me' + ' ' +
		''
	).replace(/\./g,'\\.').replace(/ /g, '|');
	return new RegExp('^https?://(?:' + fragment + ')/');
})();
function modPrt(origUrl) {
	if (HTTPS_SUPPORT_SITE_REGEX.test(origUrl)) {
		return origUrl.replace(/^https?:/, '');	
	}
	return origUrl;
}

var ACCESS_CONTROL_SUPPORT_DOMAIN_REGEX = new RegExp('^' + EXT_SERV_HOST_URL + '/');
var SUPPORT_XHR2_CROSS_DOMAIN = isSupportXhr2();

// Utils
function $E(tagName, attributes) {
	var e = $(document.createElement(tagName));
	attributes && e.attr(attributes);
	return e;
}

function callWebApi(endpoint, sendData, complete) {
	var dataType = SUPPORT_XHR2_CROSS_DOMAIN && ACCESS_CONTROL_SUPPORT_DOMAIN_REGEX.test(endpoint) ? 'json' : 'jsonp';
	ajax({
		url: endpoint, data: sendData, dataType: dataType,
		success: function(data, status, xhr) {
			complete(data, xhr && xhr.status);
		},
		error: function(xhr, status, errorThrown) {
			complete(null, xhr && xhr.status);
		}
	});
}

function baseDecode(letters, snipcode) {
	var ret = 0;
	for (var i = snipcode.length, m = 1; i; i--, m *= letters.length) {
		ret += letters.indexOf(snipcode.charAt(i-1)) * m;
	}
	return ret;
}
function base58decode(snipcode) {
	return baseDecode('123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ', snipcode);
}
function base36decode(snipcode) {
	return baseDecode('0123456789abcdefghijklmnopqrstuvwxyz', snipcode);
}

function unique(array, prop) {
	if (array == null) {
		return null;
	}
	var uniqueSet = {};
	return $.grep(array, function(value) {
		var key = value[prop];
		var result = !(key in uniqueSet);
		if (result) {
			uniqueSet[key] = true;
		}
		return result;
	});
}
function objectToArrayWithKey(obj) {
	var arry = [];
	for (var k in obj) {
		arry.push([k, obj[k]]);
	}
	return arry;
}
// /Utils

// Cache
var Cache = (function() {
	var storage = null;
	if (USE_LOCAL_STORAGE_ENABLE && window.localStorage) {
		storage = localStorage;
	} else if (window.sessionStorage) {
		storage = sessionStorage;
	}

	function constructor(cacheSizeMax, storeKey) {
		this.cacheSizeMax = cacheSizeMax;
		this.storeKey = storeKey;
		this.interval = 20 * 1000;
		this.init();
	}

	function genCache() {
		return { version: VERSION, size: 0 };
	}

	constructor.prototype = {
		init: function() {
			this.modified = false;
			this.caches = [];
			if (storage && this.storeKey) {
				var json = storage[this.storeKey];
				if (json) {
					var cache = JSON.parse(json);
					if (cache.version == VERSION) {
						this.caches.push(cache);
					}
				}
				var self = this;
				setInterval(function() {
					if (self.modified) {
						storage[self.storeKey] = JSON.stringify(self.cache());
						self.modified = false;
					}
				}, this.interval);
			}
			if (this.caches.length == 0) {
				this.caches.push(genCache());
			}
		},
		get: function(key) {
			for(var i = 0; i < this.caches.length; i++) {
				var val = this.caches[i][key];
				if (val) {
					return val;
				}
			}
			return null;
		},
		put: function(key, value) {
			this.shift();
			var cache = this.cache();
			if (!(key in cache)) {
				cache.size += 1;
			}
			cache[key] = value;
			this.modified = true;
		},
		shift: function() {
			if (this.cache().size > this.cacheSizeMax) {
				if (this.caches.unshift(genCache()) > 3) {
					this.caches.pop();
				}
				this.modified = true;
			}
		},
		cache: function() { return this.caches[0]; }
	};
	return constructor;
})();

var ExpandedUrlCache = new Cache(MAX_URL_CACHE, EXPANDED_URL_CACHE_KEY);
ExpandedUrlCache.put_orig = ExpandedUrlCache.put;
ExpandedUrlCache.put = function(key, value) {
	if (key == value) {
		return;
	}
	this.put_orig(key, value);
};
var ThumbnailUrlCache = new Cache(MAX_URL_CACHE, THUMBNAIL_URL_CACHE_KEY);
// /Cache

// WebService
function expandShortUrl(shortUrl, complete) {
	var cached = ExpandedUrlCache.get(shortUrl);
	if (cached) {
		complete(cached);
		return;
	}
	callWebApi(EXT_SERV_ENDS.expandurl, { url: shortUrl }, function(data, statusCode) {
		var longUrl = null;
		if (data) {
			longUrl = data['long-url'];
			ExpandedUrlCache.put(shortUrl, longUrl);
		}
		complete(longUrl);
	});
}

var twitcasting = {
	fetchUserImage: function(userId, complete) {
		callWebApi('http://api.twitcasting.tv/api/userstatus', { user: userId }, function(data) {
			complete(data && data.image, true);
		});
	},
	fetchMovieThumbnail: function(movieId, userId, complete) {
		var self = this;
		callWebApi('http://api.twitcasting.tv/api/moviestatus', { movieid: movieId }, function(movieData) {
			if (movieData) {
				if (movieData.thumbnailsmall) {
					complete(movieData.thumbnailsmall, true);
				} else {
					self.fetchUserImage(userId, complete);
				}
			} else {
				complete();
			}
		});
	},
	fetchLiveThumbnail: function(userId, complete) {
		var completeNoCache = function(thumbUrl) { complete(thumbUrl); }; // ignore cache
		var self = this;
		callWebApi('http://api.twitcasting.tv/api/livestatus', { user: userId }, function(liveData) {
				if (liveData) {
					if (liveData.islive) {
						completeNoCache('http://twitcasting.tv/' + userId + '/thumbstream/liveshot-1');
					} else if (liveData.movieid) {
						self.fetchMovieThumbnail(liveData.movieid, userId, completeNoCache);
					} else {
						self.fetchUserImage(userId, completeNoCache);
					}
				} else {
					completeNoCache();
				}
		});
	}
};

function fetchUstreamThumbnail(subject, uid, complete) {
	callWebApi('http://api.ustream.tv/json/' + subject + '/' + uid + '/getValueOf/imageUrl', {}, function(data) {
		complete(data && data.small, true);
	});
}

function resolvOgmediaDefault(matched, complete) {
	callWebApi(EXT_SERV_ENDS.ogmedia, { url: matched[0] }, function(data) {
		complete(data && data.url, true);
	});
}
// /WebService

// ShortUrlRegexs
var SHORT_URL_REGEX = (function() {
	var fragment = (
		'bit.ly j.mp t.co htn.to ux.nu ustre.am am6.jp ow.ly ht.ly fb.me lnk.ms dai.ly a.r10.to' + ' ' +
		'cot.ag p.tl amzn.to dlvr.it goo.gl moi.st dailybooth.com/u tinyurl.com sgp.cm pbckt.com' + ' ' +
		'fon.gs mysp.ac itun.es is.gd v.gd r.sm3.jp tou.ch po.st post.ly pocket.co'
	).replace(/\./g,'\\.').replace(/ /g, '|');
	return new RegExp('^http://(?:' + fragment + ')/[\\w\\-:\\.]+');
})();
// /ShortUrlRegexs

// ThumbnailResolvers
var THUMB_RESOLVERS = {
	allimg: {
		priority: -1,
		regex: /^https?:\/\/[^?]+?\.(?:jpe?g|png|gif|bmp|JPE?G|PNG|GIF|BMP)(?=\?|$)/,
		resolv: function(matched, complete) {
			complete(matched[0]);
		}
	},
	twitpic: {
		priority: 2,
		regex: /^http:\/\/twitpic\.com\/(\w+)/,
		regexThumb: /^http:\/\/twitpic\.com\/show\/thumb\/\w+/,
		resolv: function(matched, complete) {
			complete('http://twitpic.com/show/thumb/' + matched[1]);
		}
	},
	imgly: {
		regex: /^http:\/\/img\.ly\/(\w+)/,
		regexThumb: /^http:\/\/img\.ly\/show\/thumb\/\w+/,
		resolv: function(matched, complete) {
			complete('http://img.ly/show/thumb/' + matched[1]);
		}
	},
	twitgoo: {
		regex: /^http:\/\/twitgoo\.com\/(\w+)/,
		regexThumb: /^http:\/\/twitgoo\.com\/show\/thumb\/\w+/,
		resolv: function(matched, complete) {
			complete('http://twitgoo.com/show/thumb/' + matched[1]);
		}
	},
	picim: {
		regex: /^http:\/\/pic\.im\/(\w+)/,
		regexThumb: /^http:\/\/pic\.im\/website\/thumbnail\/\w+/,
		resolv: function(matched, complete) {
			complete('http://pic.im/website/thumbnail/' + matched[1]);
		}
	},
	youtube: {
		regex: /^(?:http:\/\/www\.youtube\.com\/watch\/?\?v=([\w\-]+))|(?:http:\/\/youtu\.be\/([\w\-]+))/,
		resolv: function(matched, complete) {
			var id = matched[2] || matched[1];
			complete('http://i.ytimg.com/vi/' + id + '/default.jpg');
		}
	},
	imgur: {
		regex: /^http:\/\/imgur\.com\/(?:r\/pics\/)?(\w+)(?!\/)/,
		resolv: function(matched, complete) {
			complete('http://i.imgur.com/' + matched[1] + 't.jpg');
		}
	},
	owly: {
		regex: /^http:\/\/ow\.ly\/i\/(\w+)/,
		resolv: function(matched, complete) {
			complete('http://static.ow.ly/photos/thumb/' + matched[1] + '.jpg');
		}
	},
	movapic: {
		regex: /^http:\/\/movapic\.com\/pic\/(\w+)/,
		resolv: function(matched, complete) {
			complete('http://image.movapic.com/pic/s_' + matched[1] + '.jpeg');
		}
	},
	hatena: {
		regex: /^http:\/\/f\.hatena\.ne\.jp\/([\w\-]+)\/(\d+)/,
		resolv: function(matched, complete) {
			complete(['http://cdn-ak.f.st-hatena.com/images/fotolife', matched[1].charAt(0), matched[1], matched[2].substr(0, 8), matched[2]].join('/') + '_120.jpg');
		}
	},
	moby: {
		regex: /^http:\/\/moby\.to\/(\w+)/,
		resolv: function(matched, complete) {
			complete(matched[0] + ':square');
		}
	},
	yfrog: {
		regex: /^http:\/\/yfrog\.com\/\w+/,
		resolv: function(matched, complete) {
			complete(matched[0] + ':small');
		}
	},
	plixi: {
		regex: /^http:\/\/plixi\.com\/p\/(\w+)/,
		regexThumb: /^http:\/\/api\.plixi\.com\/api\/TPAPI\.svc\/imagefromurl\?size=thumbnail&url=http:\/\/plixi\.com\/p\/\w+/,
		resolv: function(matched, complete) {
			complete('http://api.plixi.com/api/TPAPI.svc/imagefromurl?size=thumbnail&url=' + matched[0]);
		}
	},
	nicovideo: {
		regex: /^(?:http:\/\/www\.nicovideo\.jp\/watch\/(?:sm|nm)(\d+))|(?:http:\/\/nico\.ms\/(?:sm|nm)(\d+))/,
		regexThumb: /^http:\/\/tn-skr\d?\.smilevideo\.jp\/smile\?i=\d+/,
		resolv: function(matched, complete) {
			var id = matched[2] || matched[1];
			complete('http://tn-skr.smilevideo.jp/smile?i=' + id);
		}
	},
	nicovideoapi: {
		regex: /^(?:http:\/\/(?:www|live)\.nicovideo\.jp\/(?:watch|gate)\/((?:lv)?\d+))|(?:http:\/\/nico\.ms\/((?:lv)?\d+))/,
		resolv: function(matched, complete) {
			var id = matched[2] || matched[1];
			callWebApi(EXT_SERV_ENDS.nicovideo, { id: id }, function(data) {
				complete(data && data.url, true);
			});
		}
	},
	nicoseiga: {
		regex: /^(?:http:\/\/seiga\.nicovideo\.jp\/seiga\/im(\d+))|(?:http:\/\/nico\.ms\/im(\d+))/,
		regexThumb: /^http:\/\/lohas\.nicoseiga\.jp\/thumb\/\d+[qi]/,
		resolv: function(matched, complete) {
			var id = matched[2] || matched[1];
			complete('http://lohas.nicoseiga.jp/thumb/' + id + 'q');
		}
	},
	nicocomu: {
		regex: /^http:\/\/com\.nicovideo\.jp\/community\/co(\d+)/,
		resolv: function(matched, complete) {
			complete('http://icon.nimg.jp/community/co' + matched[1] + '.jpg');
		}
	},
	twipple: {
		regex: /^http:\/\/p\.twipple\.jp\/(\w+)$/,
		regexThumb: /^http:\/\/p\.twpl\.jp\/show\/thumb\/\w+/,
		resolv: function(matched, complete) {
			complete('http://p.twpl.jp/show/thumb/' + matched[1]);
		}
	},
	photozou: {
		regex: /^http:\/\/photozou\.jp\/photo\/show\/\d+\/(\d+)/,
		regexThumb: /^http:\/\/photozou\.jp\/p\/thumb\/\d+/,
		resolv: function(matched, complete) {
			complete('http://photozou.jp/p/thumb/' + matched[1]);
		}
	},
	ustream: {
		regex: /^http:\/\/(?:www\.)?ustream\.tv\/(channel|recorded)\/(?:id\/)?([\w\-%]+)/,
		subjects: { 'channel': 'channel', 'recorded': 'video' },
		resolv: function(matched, complete) {
			fetchUstreamThumbnail(this.subjects[matched[1]], matched[2], complete);
		}
	},
	lockerz: {
		regex: /^http:\/\/lockerz\.com\/s\/\d+/,
		regexThumb: /^http:\/\/api\.plixi\.com\/api\/tpapi\.svc\/imagefromurl\?size=thumbnail&url=http:\/\/lockerz\.com\/s\/\d+/,
		resolv: function(matched, complete) {
			complete('http://api.plixi.com/api/tpapi.svc/imagefromurl?size=thumbnail&url=' + matched[0]);
		}
	},
	jcomi: {
		regex: /^http:\/\/(?:www\.|vw\.)?j-comi\.jp\/(?:book|murasame)\/(?:comic|detail|view)\/\d+/,
		resolv: resolvOgmediaDefault
	},
	picplz: {
		regex: /^http:\/\/picplz\.com\/(?:user\/\w+\/pic\/(\w+)|(\w+))/,
		resolv: function(matched, complete) {
			var param = {};
			if (matched[1]) {
				param.longurl_id = matched[1];
			} else {
				param.shorturl_id = matched[2];
			}
			callWebApi('http://api.picplz.com/api/v2/pic.json', param, function(data) {
				var imgUrl = null;
				if (data && data.result == 'ok') {
					imgUrl = data.value.pics[0].pic_files['100sh'].img_url;
				};
				complete(imgUrl, true);
			});
		}
	},
	kabegami: {
		regex: /^http:\/\/www\.kabegami\.com\/[\w\-]+\/\w+\/show\/id\/(PHOT(\d{10})(\w\w)(\w\w)\w\w)/,
		resolv: function(matched, complete) {
			complete(['http://www.kabegami.com/content_image/phot', matched[2], matched[3], matched[4], matched[1]].join('/') + '_s90.jpg');
		}
	},
	instagram: {
		regex: /^http:\/\/(?:instagr\.am|instagram\.com)\/p\/[\w\-]+/,
		resolv: function(matched, complete) {
			complete(matched[0] + '/media/?size=t');
		}
	},
	mypix: {
		regex: /^http:\/\/www\.mypix\.jp\/app\.php\/picture\/\d+/,
		resolv: function(matched, complete) {
			complete(matched[0] + '/thumbx.jpg');
		}
	},
	fotolog: {
		regex: /^http:\/\/fotolog\.cc\/(\w+)/,
		regexThumb: /^http:\/\/fotolog\.cc\/image\/\w+\/mini/,
		resolv: function(matched, complete) {
			complete('http://fotolog.cc/image/' + matched[1] + '/mini');
		}
	},
	vimeo: {
		regex: /^http:\/\/(?:www\.)?vimeo\.com\/(\d+)/,
		resolv: function(matched, complete) {
			callWebApi('http://vimeo.com/api/v2/video/' + matched[1] + '.json', {}, function(data) {
				complete(data && data[0].thumbnail_small, true);
			});
		}
	},
	dailybooth: {
		regex: /^http:\/\/dailybooth\.com\/[\w\-]{2,}\/(\d+)/,
		resolv: function(matched, complete) {
			callWebApi('http://api.dailybooth.com/v2/pictures/' + matched[1] + '.json', {}, function(data) {
				complete(data && data.urls.small, true);
			});
		}
	},
	twitcasting: {
		regex: /^http:\/\/twitcasting\.tv\/([\w\-]+)\/movie\/(\d+)/,
		resolv: function(matched, complete) {
			var userId = matched[1];
			var movieId = matched[2];
			twitcasting.fetchMovieThumbnail(movieId, userId, complete);
		}
	},
	twitcastinglive: {
		regex: /^http:\/\/twitcasting\.tv\/([\w\-]+)\/?$/,
		resolv: function(matched, complete) {
			var userId = matched[1];
			twitcasting.fetchLiveThumbnail(userId, complete);
		}
	},
	metacafe: {
		regex: /^http:\/\/www\.metacafe\.com\/(?:w|watch)\/([\w\-]+)/,
		resolv: function(matched, complete) {
			complete('http://www.metacafe.com/thumb/' + matched[1] + '.jpg');
		}
	},
	dailymotion: {
		regex: /^http:\/\/(?:www|touch)\.dailymotion\.com\/(?:#\/)?video\/([\w\-]+)/,
		resolv: function(matched, complete) {
			callWebApi('https://api.dailymotion.com/video/' + matched[1], { fields: 'thumbnail_medium_url' }, function(data) {
				complete(data && data.thumbnail_medium_url, true);
			});
		}
	},
	stickamjpprof: {
		regex: /^http:\/\/(?:www\.stickam\.jp\/profile|stick\.am\/p)\/(\w+)/,
		resolv: function(matched, complete) {
			callWebApi('http://api.stickam.jp/api/user/' + matched[1] + '/profile', { mime: 'json' }, function(data) {
				complete(data && data.profile_image, true);
			});
		}
	},
	stickamjpmedia: {
		regex: /^http:\/\/www\.stickam\.jp\/video\/(\d+)/,
		resolv: function(matched, complete) {
			callWebApi('http://api.stickam.jp/api/media/' + matched[1], { mime: 'json' }, function(data) {
				complete(data && data.media && data.media.thumb, true);
			});
		}
	},
	photobucket: {
		regex: /^http:\/\/[is]\d+\.photobucket\.com\/albums\/.+/,
		resolv: function(matched, complete) {
			callWebApi('http://photobucket.com/oembed', { url: matched[0] }, function(data) {
				complete(data && data.thumbnail_url, true);
			});
		}
	},
	pixiv: {
		regex: /^http:\/\/(?:www\.pixiv\.net\/member_illust\.php\/?\?(?:mode=medium&)?illust_id=|p\.tl\/i\/)(\d+)/,
		resolv: function (matched, complete) {
			callWebApi(EXT_SERV_ENDS.ogmedia, { url: 'http://www.pixiv.net/member_illust.php?mode=medium&illust_id=' + matched[1] }, function(data) {
				complete(data && data.url, true);
			});
		}
	},
	flickr: {
		regex: /^http:\/\/(?:www\.flickr\.com\/photos\/[\w@\-]+\/(\d+))|(?:flic\.kr\/p\/(\w+))/,
		resolv: function(matched, complete) {
			var id = matched[2] ? base58decode(matched[2]) : matched[1];
			callWebApi(EXT_SERV_ENDS.flickr, { photo_id: id }, function(data) {
				complete(data && data.url, true);
			});
		}
	},
	videosurf: {
		regex: /^http:\/\/www\.videosurf\.com\/video\/-?(?:[a-zA-Z0-9%]+-)*(\d+)\b/,
		resolv: function(matched, complete) {
			callWebApi(EXT_SERV_ENDS.videosurf, { id: matched[1] }, function(data) {
				complete(data && data.url, true);
			});
		}
	},
	tinami: {
		regex: /^http:\/\/(?:www\.tinami\.com\/view\/(\w+)|tinami\.jp\/(\w+))/,
		resolv: function(matched, complete) {
			var id = matched[2] ? base36decode(matched[2]) : matched[1];
			callWebApi(EXT_SERV_ENDS.tinami, { id: id }, function(data) {
				complete(data && data.url, true);
			});
		}
	},
	akibablog: {
		regex: /^http:\/\/blog\.livedoor\.jp\/geek\/archives\/\d+\.html/,
		resolv: resolvOgmediaDefault
	},
	photobucketmedia: {
		regex: /^http:\/\/media\.photobucket\.com\/.+/,
		resolv: resolvOgmediaDefault
	},
	ustreamsp: {
		regex: /^http:\/\/(?:www\.)?ustream\.tv\/[\w\-%]+(?=\/?$)/,
		resolv: function(matched, complete) {
			callWebApi(EXT_SERV_ENDS.ustream, { url: matched[0] }, function(data) {
				if (data) {
					fetchUstreamThumbnail('channel', data.channelId, complete);
				} else {
					complete();
				}
			});
		}
	},
	tumblr: {
		regex: /^http:\/\/([\w\-]+\.tumblr\.com)\/post\/(\d+)/,
		resolv: function(matched, complete) {
			callWebApi(EXT_SERV_ENDS.tumblr, { base_hostname: matched[1], post_id: matched[2] }, function(data) {
					complete(data && data.url, true);
			});
		}
	},
	tumblrs: {
		regex: /^http:\/\/(?:tumblr\.com|tmblr\.co)\/[\w\-]+/,
		resolv: function(matched, complete) {
			expandShortUrl(matched[0], function(longUrl) {
				var regex = /^http:\/\/((?:\.?[\w\-]+)+)\/post\/(\d+)/;
				if (regex.test(longUrl)) {
					THUMB_RESOLVERS.tumblr.resolv(longUrl.match(regex), complete);
				} else {
					complete();
				}
			});
		}
	},
	rakutenbooks: {
		regex: /^http:\/\/books\.rakuten\.co\.jp\/rb\/[\w%\-]+\-(\d+)\//,
		resolv: function(matched, complete) {
			callWebApi(EXT_SERV_ENDS.rakuten, { type: 'book', id: matched[1] }, function(data) {
				complete(data && data.url, true);
			});
		}
	},
	rakutenitem: {
		regex: /^http:\/\/item\.rakuten\.co\.jp\/([\w\-]+\/[\w\-]+)/,
		resolv: function(matched, complete) {
			callWebApi(EXT_SERV_ENDS.rakuten, { type: 'item', id: matched[1] }, function(data) {
				complete(data && data.url, true);
			});
		}
	},
	twil: {
		regex: /^http:\/\/shlink\.st\/[\w\-]+/,
		resolv: function(matched, complete) {
			callWebApi(EXT_SERV_ENDS.twil, { url: matched[0] }, function(data) {
				complete(data && data.thumbnail_url, true);
			});
		}
	},
	twitrpix: {
		regex: /^http:\/\/twitrpix\.com\/(\w+)/,
		regexThumb: /^http:\/\/img\.twitrpix\.com\/thumb\/\w+/,
		resolv: function(matched, complete) {
			complete('http://img.twitrpix.com/thumb/' + matched[1]);
		}
	},
	pikchur: {
		regex: /^http:\/\/(?:pikchur\.com|pk\.gd)\/(\w+)/,
		resolv: function(matched, complete) {
			complete(' http://img.pikchur.com/pic_' + matched[1] + '_s.jpg');
		}
	},
	twitxr: {
		regex: /^http:\/\/twitxr\.com\/(\w+)\/updates\/(\d+)/,
		regexThumb: /^http:\/\/twitxr\.com\/image\/\d+\/th/,
		resolv: function(matched, complete) {
			complete('http://twitxr.com/image/' + matched[2] + '/th');
		}
	},
	myspacevideo: {
		regex: /^http:\/\/(?:www\.)?myspace\.com\/video\/(?:(vid|rid)|[\w-]+\/[\w-]+)\/(\d+)/,
		resolv: function(matched, complete) {
			var id = (matched[1] || 'vid') + '/' + matched[2];
			callWebApi(EXT_SERV_ENDS.myspacevideo, { id: id }, function(data) {
				complete(data && data.url, true);
			});
		}
	},
	myspacevideovids: {
		regex: /^http:\/\/(?:vids\.|www\.)?myspace\.com\/index\.cfm\?fuseaction=vids\.individual&videoid=(\d+)/,
		resolv: function(matched, complete) {
			var id = 'vid/' + matched[1];
			callWebApi(EXT_SERV_ENDS.myspacevideo, { id: id }, function(data) {
				complete(data && data.url, true);
			});
		}
	},
	twitvideo: {
		regex: /^http:\/\/twitvideo\.jp\/(\w+)/,
		regexThumb: /^http:\/\/twitvideo\.jp\/img\/thumb\/\w+/,
		resolv: function(matched, complete) {
			complete('http://twitvideo.jp/img/thumb/' + matched[1]);
		}
	},
	mycom: {
		regex: /^http:\/\/(?:if\.|s\.)?journal\.mycom\.co\.jp\/\w+\/\d+\/\d+\/\d+\/\w+\//,
		resolv: function(matched, complete) {
			complete(matched[0] + 'index.top.jpg');
		}
	},
	twitvid: {
		regex: /^http:\/\/(?:www\.)?twitvid\.com\/(?!videos)(\w+)/,
		resolv: function(matched, complete) {
			var id = matched[1];
			complete(['http://llphotos.twitvid.com/twitvidthumbnails', id.charAt(0), id.charAt(1), id.charAt(2), id].join('/') + '_med.jpg');
		}
	},
	pckles: {
		regex: /^http:\/\/(?:pckl\.es|pckles\.com)\/\w+\/\w+/,
		resolv: function(matched, complete) {
			complete(matched[0] + '.thumb.jpg');
		}
	},
	itunes: {
		regex: /^http:\/\/(?:c\.)?itunes\.apple\.com\/.*\/id\w+(?:\?i=\d+)?/,
		resolv: function(matched, complete) {
			callWebApi(EXT_SERV_ENDS.itunes, { url: matched[0] }, function(data) {
				complete(data && data.url, true);
			});
		}
	},
	loctouch: {
		regex: /^http:\/\/tou\.ch\/spot\/\d+\/(\w+)\/(\d+)/,
		resolv: function(matched, complete) {
			callWebApi(EXT_SERV_ENDS.loctouch, { type: matched[1], id: matched[2] }, function(data) {
				complete(data && data.url, true);
			});
		}
	},
	miil: {
		regex: /^http:\/\/miil\.me\/p\/(\w+)/,
		resolv: function(matched, complete) {
			complete(matched[0] + '.jpeg?size=150');
		}
	},
	i500px: {
		regex: /^https?:\/\/(?:www\.)?500px\.com\/photo\/(\d+)/,
		resolv: function(matched, complete) {
			callWebApi(EXT_SERV_ENDS.i500px, { id: matched[1] }, function(data) {
				complete(data && data.url, true);
			});
		}
	},
	hulu: {
		regex: /^http:\/\/(?:www\.hulu\.com\/watch|hulu\.com\/w)\/.*/,
		resolv: function(matched, complete) {
			callWebApi('http://www.hulu.com/api/oembed', { url: matched[0] }, function(data) {
				complete(data && data.thumbnail_url, true);
			});
		}
	},
	facebook: {
		regex: /^https?:\/\/www\.facebook\.com\/photo\.php\?fbid=(\w+)/,
		resolv: function(matched, complete) {
			callWebApi(EXT_SERV_ENDS.facebook, { id: matched[1] }, function(data) {
				complete(data && data.url, true);
			});
		}
	},
	immio: {
		regex: /^http:\/\/imm\.io\/\w+/,
		resolv: resolvOgmediaDefault
	},
	comicdash: {
		regex: /^http:\/\/ckworks\.jp\/comicdash\/series\/\d+/,
		resolv: resolvOgmediaDefault
	},
	gyazo: {
		regex: /^http:\/\/gyazo\.com\/\w+/,
		resolv: function(matched, complete) {
			complete(matched[0] + '.png');
		}
	},
	viame: {
		regex: /^http:\/\/via\.me\/-\w+/,
		resolv: resolvOgmediaDefault
	},
	posterous: {
		regex: /^http:\/\/[\w\-]+\.posterous\.com\/[\w\-]+/,
		resolv: resolvOgmediaDefault
	},
	bookmetercmt: {
		regex: /^http:\/\/book\.akahoshitakuya\.com\/cmt\/\w+/,
		resolv: resolvOgmediaDefault
	},
	booklogp: {
		regex: /^http:\/\/p\.booklog\.jp\/book\/\d+/,
		resolv: function(matched, complete) {
			callWebApi(EXT_SERV_ENDS.ogmedia, { url: matched[0] }, function(data) {
				var imgUrl = null;
				if (data && data.url) {
					imgUrl = data.url.replace(/_s\.jpg$/, '_m.jpg');
				}
				complete(imgUrl, true);
			});
		}
	},
	booklogitem: {
		regex: /^http:\/\/booklog\.jp\/item\/3\/\d+/,
		resolv: resolvOgmediaDefault
	},
	mlkshk: {
		regex: /^http:\/\/mlkshk\.com\/\w\/(\w+)/,
		resolv: function(matched, complete) {
			complete('http://mlkshk.com/r/' + matched[1]);
		}
	},
	twitdraw: {
		regex: /^http:\/\/twitdraw\.com\/(\w+)/,
		resolv: function(matched, complete) {
			complete('http://td-images.s3.amazonaws.com/' + matched[1] + '.png');
		}
	},
	tegaki: {
		regex: /^http:\/\/tegaki\.pipa\.jp\/\d+\/(\d+)\.html/,
		resolv: function(matched, complete) {
			complete('http://tegaki.pipa.jp/CGetBlogImgS.jsp?TD=' + matched[1]);
		}
	},
	gizmodo: {
		regex: /^http:\/\/(?:www|us)\.gizmodo\.(?:jp|co\.uk|de|com)\/\d+\/(?:\d+\/)?[\w\.\-]+/,
		resolv: resolvOgmediaDefault
	},
	piapro: {
		regex: /^http:\/\/piapro\.jp\/t\/[\w\-]+/,
		resolv: resolvOgmediaDefault
	},
	tweetvite: {
		regex: /^http:\/\/(?:tweetvite\.com\/event|twvt\.us)\/([\w\-]+)/,
		resolv: function resolvOgmediaDefault(matched, complete) {
			var url = 'http://tweetvite.com/event/' + matched[1];
			callWebApi(EXT_SERV_ENDS.ogmedia, { url: url }, function(data) {
				complete(data && data.url, true);
			});
		}
	},
	twitter: {
		priority: 1,
		regex: /^https?:\/\/twitter\.com\/(?:#!\/)?[\w\-]+\/status\/(\d+)\/photo\/1/,
		resolv: function(matched, complete) {
			callWebApi(EXT_SERV_ENDS.pictwitter, { id: matched[1] }, function(data) {
				complete(data && data.url, true);
			});
		}
	},
	eyeem: {
		regex: /^http:\/\/www\.eyeem\.com\/p\/\d+/,
		resolv: resolvOgmediaDefault
	},
	vine: {
		regex: /^https?:\/\/vine\.co\/v\/\w+/,
		resolv: resolvOgmediaDefault
	}
};
// amazon
(function(resolvers) {
	var fetchAmazonThumbnail = function(sendData, complete) {
		callWebApi(EXT_SERV_ENDS.amazon, sendData, function(data) {
			complete(data && data.url, true);
		});
	};
	var resolvAmazonThumbnailJpDefault = function(matched, complete) {
		fetchAmazonThumbnail({ tld: 'jp', asin: matched[1] }, complete);
	};

	resolvers.amazon = {
		regex: /^http:\/\/(?:www\.)?amazon(?:\.co)?\.(jp|com|ca|cn|de|fr|it|uk)\/(?:(?:(?:gp|dp)\/product(?:-\w+)?|o\/ASIN|exec\/obidos\/ASIN)\/(\w+)|(?:[^\/]+\/)?dp\/(\w+))/,
		resolv: function(matched, complete) {
			fetchAmazonThumbnail({ tld: matched[1], asin: matched[3] || matched[2] }, complete);
		}
	};
	resolvers.bookmeter = {
		regex: /^http:\/\/book\.akahoshitakuya\.com\/b\/(\w+)/,
		resolv: resolvAmazonThumbnailJpDefault
	};
	resolvers.mediamarker = {
		regex: /^http:\/\/mediamarker\.net\/\w\/[\w\-]+\/\?asin=(\w+)/,
		resolv: resolvAmazonThumbnailJpDefault
	};
	resolvers.booklog = {
		regex: /^http:\/\/booklog\.jp\/(?:users\/[\w\-]+\/archives|asin|item)\/1\/(\w+)/,
		resolv: resolvAmazonThumbnailJpDefault
	};
	resolvers.sociallibrary = {
		regex: /^http:\/\/www\.sociallibrary\.jp\/entry\/(\w+)/,
		resolv: resolvAmazonThumbnailJpDefault
	};
})(THUMB_RESOLVERS);
// /amazon

// /ThumbnailResolvers

// Process
var Process = function(args) {
	this.waitCount = 0;
	this.results = [];
	this.split = args.split;
	this.execute = args.execute;
	this.complete = args.complete;
	this.queue = args.queue;
};
Process.prototype = {
	run: function(input) {
		var subInputs = this.split(input);
		var inputLength = subInputs ? subInputs.length : 0;
		this.waitCount = inputLength;
		this.internalComplete();
		for (var i = 0; i < inputLength; i++) {
			this.execute(subInputs[i]);
		}
	},
	emitComplete: function(result) {
		this.waitCount--;
		if (result) {
			this.results.push(result);
		}
		this.internalComplete();
	},
	internalComplete: function() {
		if (this.waitCount == 0) {
			if (this.complete) {
				this.complete(this.results);
			}
			if (this.queue.length != 0) {
				this.queue.shift()();
			}
		}
	}
};
// /Process

// ExpandThumbnailMainProcess
var thumbResolverWithKeyArry = objectToArrayWithKey(THUMB_RESOLVERS);
thumbResolverWithKeyArry.sort(function(a, b) {
	var a_p = 'priority' in a[1] ? a[1].priority : 0;
	var b_p = 'priority' in b[1] ? b[1].priority : 0;
	if (a_p < b_p) return 1;
	if (a_p > b_p) return -1;
	return 0;
});
function resolvThumbnailUrl(existsOfficalThumbnails, contentUrl, complete) {
	var cached = ThumbnailUrlCache.get(contentUrl);
	if (cached) {
		complete(cached);
		return true;
	}
	var match = false;
	$.each(thumbResolverWithKeyArry, function(index, resolverWithKey) {
		var resolver = resolverWithKey[1];
		if (resolver.regex.test(contentUrl)) {
			if (existsOfficalThumbnails && domainEnv.supportedThumbnails && resolverWithKey[0] in domainEnv.supportedThumbnails) {
				return false;
			}
			resolver.resolv(contentUrl.match(resolver.regex), function(thumbnailUrl, cache) {
				if (thumbnailUrl && cache) {
					ThumbnailUrlCache.put(contentUrl, thumbnailUrl);
				}
				complete(thumbnailUrl);
			});
			match = true;
			return false;
		} else if (resolver.regexThumb && resolver.regexThumb.test(contentUrl)) {
			complete(contentUrl.match(resolver.regexThumb)[0]);
			match = true;
			return false;
		}
	});
	return match;
}

function createThumbnailElement(urlEntries) {
	var ul = $E('ul', { 'class': 'ithumb-ul' });
	for (var i = 0; i < urlEntries.length; i++) {
		var urlEntry = urlEntries[i];
		ul.append(
			$E('li', { 'class':'ithumb-li' }).append(
				$E('a', { 'class':'ithumb-a', 'target':'_blank', 'href':urlEntry.url, 'rel':'url' }).append(
					$E('img', { 'src': modPrt(urlEntry.thumbUrl), 'class':'ithumb-img' })
						.error(function() {
							var img = $(this);
							var tryload = img.data('tryload') || 0;
							if (tryload < 2) {
								setTimeout(function() {
									img.data('tryload', tryload + 1).attr('src', img.attr('src'));
								}, 7000);
							} else {
								img.hide();
							}
						})
				)
			)
		);
	}
	var thumb = $E('div', { 'class':'ithumb-container' }).append(ul);
	if (domainEnv.customizeThumbnailElement) {
		domainEnv.customizeThumbnailElement(thumb);
	}
	return thumb;
}

function expandThumbnail(contentElement, existsOfficalThumbnails) {
	var urlStack = [];
	var processQueue = [];

	/* <thumbnailExpandProcess> */
	var urlExtractProcess = new Process({
		queue: processQueue,
		split: function(element) {
			return element.find('a').map(function() {
				var anchor = $(this);
				var url = anchor.attr('href');
				return (url && (/^https?:\/\/(?!twitter\.com\/#!\/)/).test(url)) ? anchor : null;
			});
		},
		execute: function(anchor) {
			var urlEntry = { thumbUrl: null, expandCount: 0 };
			var expandedUrl = null;
			if (domainEnv.getExpandedUrl) {
				expandedUrl = domainEnv.getExpandedUrl(anchor);
			}
			urlEntry.url = expandedUrl || anchor.attr('href');
			this.emitComplete(urlEntry);
		},
		complete: function(urlEntries) {
			urlStack = unique(urlEntries, 'url');
		}
	});

	var thumbnailUrlResolveProcess = new Process({
		queue: processQueue,
		split: function(urlEntries) {
			return urlEntries;
		},
		execute: function (urlEntry) {
			var self = this;
			var match = resolvThumbnailUrl(existsOfficalThumbnails, urlEntry.url, function(thumbUrl) {
				urlEntry.thumbUrl = thumbUrl;
				self.emitComplete();
			});
			if (!match) {
				if (urlEntry.expandCount < 5 && SHORT_URL_REGEX.test(urlEntry.url)) {
					expandShortUrl(urlEntry.url, function(longUrl) {
						if (urlEntry.url != longUrl) {
							urlEntry.expandCount += 1;
							urlEntry.url = longUrl;
							self.execute(urlEntry);
						} else {
							self.emitComplete();
						}
					});
				} else {
					self.emitComplete();
				}
			}
		},
	});
	/* </thumbnailExpandProcess> */

	processQueue.push(function() { thumbnailUrlResolveProcess.run(urlStack); });
	processQueue.push(function() {
		var urlEntries = unique(
												$.grep(urlStack, function(val) { return val.thumbUrl; }),
												'thumbUrl');
		if (urlEntries.length != 0) {
			domainEnv.appendThumbnail(contentElement, createThumbnailElement(urlEntries));
		}
	});

	urlExtractProcess.run(contentElement);
}
// /ExpandThumbnailMainProcess

var CSS_URL_DEFAULT = EXT_SERV_HOST_URL + '/stylesheets/inlinethumbnail/1.1.0/default.css';
var APPEND_THUMBNAIL_DEFAULT = function(contentElement, thumbnailElement) { contentElement.append(thumbnailElement); };
var DOMAIN_ENVS = {
	hootsuite: {
		hostname: 'hootsuite.com',
		select: function(context) {
			return $('._baseTweetText:not([expanded-img])', context);
		},
		appendThumbnail: APPEND_THUMBNAIL_DEFAULT,
		cssUrl: CSS_URL_DEFAULT
	},
	twitter: {
		hostname: 'twitter.com',
		select: function(context) {
			return $('.tweet:not(.simple-tweet) > .content > p:not([expanded-img])', context);
		},
		appendThumbnail: APPEND_THUMBNAIL_DEFAULT,
		getExpandedUrl: function(anchor) { return anchor.data('expanded-url'); },
		cssUrl: EXT_SERV_HOST_URL + '/stylesheets/inlinethumbnail/1.6.2/twittercom.css',
		existsOfficalThumbnails: function(contentElement) {
			return contentElement.nextAll('.cards-media-container').length > 0;
		},
		supportedThumbnails: {
			'twitter' : true
		}
	},
	twitter_mobile: {
		hostname: 'mobile.twitter.com',
		select: function(context) {
			return $('.tweet-text:not([expanded-img])', context);
		},
		appendThumbnail: APPEND_THUMBNAIL_DEFAULT,
		getExpandedUrl: function(anchor) { return anchor.data('url'); },
		cssUrl: CSS_URL_DEFAULT
	},
	crowy: {
		hostname: 'www.crowy.net',
		select: function(context) {
			return $('.message-text:not([expanded-img])', context);
		},
		appendThumbnail: APPEND_THUMBNAIL_DEFAULT,
		cssUrl: CSS_URL_DEFAULT,
		existsOfficalThumbnails: function(contentElement) {
			return contentElement.nextAll('.images').length > 0;
		},
		supportedThumbnails: {
			'twitter' : true,
			'twitpic' : true,
			'youtube' : true,
			'amazon' : true
		}
	},
	twipple: {
		hostname: 'twipple.jp',
		select: function(context) {
			return $('.tweetBox:not([expanded-img])', context.parentNode);
		},
		appendThumbnail: APPEND_THUMBNAIL_DEFAULT,
		cssUrl: CSS_URL_DEFAULT,
		existsOfficalThumbnails: function(contentElement) {
			return contentElement.find('.thumbBoxImage').length > 0;
		},
		supportedThumbnails: {
			'twitter' : true,
			'twitpic' : true,
			'youtube' : true
		}
	},
	tweetdeck: {
		hostname: 'tweetdeck.twitter.com',
		select: function(context) {
			return $('.tweet-body:not([expanded-img])', context.parentNode);
		},
		appendThumbnail: function(contentElement, thumbnailElement) { contentElement.after(thumbnailElement); },
		getExpandedUrl: function(anchor) { return anchor.data('full-url'); },
		cssUrl: CSS_URL_DEFAULT,
		existsOfficalThumbnails: function(contentElement) {
			return contentElement.children('.media-preview').length > 0;
		},
		supportedThumbnails: {
			'twitter' : true,
			'twitpic' : true,
			'youtube' : true
		}
	}
};

var domainEnv = null;
$.each(DOMAIN_ENVS, function() {
	if (this.hostname == location.hostname) {
		if (this.match) {
			if (this.match()) {
				domainEnv = this;
				return false;
			}
		} else {
			domainEnv = this;
			return false;
		}
	}
});

function applyElements(context) {
	domainEnv.select(context).each(function() {
		var contentElement = $(this);
		expandThumbnail(
			contentElement.attr('expanded-img', 'expanded-img'),
			domainEnv.existsOfficalThumbnails && domainEnv.existsOfficalThumbnails(contentElement));
	});
}

$(document.getElementsByTagName('head')[0]).append($E('link', { 'rel':'stylesheet', 'type':'text/css', 'media':'screen', 'href':domainEnv.cssUrl }));

var ApplyQueue = {
	timeoutId: null,
	queue: [],
	apply: function() {
		var targets = this.queue.splice(0, this.queue.length);
		applyElements(targets); 
	},
	push: function(elem) {
		if (this.timeoutId) {
			clearTimeout(this.timeoutId);
			this.timeoutId = null;
		}
		this.queue.push(elem);

		var self = this;
		this.timeoutId = setTimeout(function() {
			self.apply();
		}, 1000);
	}
};

// initial apply
ApplyQueue.push(document);

$(document).bind('DOMNodeInserted', function(e) {
	ApplyQueue.push(e.target);
});

} // /main logic

// load
(function mainloader(tryCount, loaded) {
	if (tryCount < 30 && !(window.jQuery)) {
		setTimeout(function() { mainloader(tryCount + 1, loaded); }, 60);
		return;
	}
	if (!loaded && !(window.jQuery)) {
		loadJQuery();
		setTimeout(function() { mainloader(0, true); }, 60);
		return;
	}
	var hostname = 'thumbnailurlconv.appspot.com';
	var endpoint = '//' + hostname + '/endpoint';
	var dataType = isSupportXhr2() ? 'json' : 'jsonp';
	ajax({ url: endpoint, dataType: dataType,
		success: function(data) { main('//' + (data ? data.hostname : hostname)); },
		error: function() { main('//' + hostname); }
	});
})(0);

} // /source code

var script = document.createElement('script');
script.type = 'text/javascript';
script.innerHTML = '(' + source.toString() + ')();';
document.body.appendChild(script);

})();