Inline Thumbnail

Expand to view thumbnails in the row.

  1. // ==UserScript==
  2. // @name Inline Thumbnail
  3. // @namespace inline_thumbnail
  4. // @description Expand to view thumbnails in the row.
  5. // @run-at document-end
  6. // @include http://hootsuite.com/*
  7. // @include https://hootsuite.com/*
  8. // @include http://twitter.com/*
  9. // @include https://twitter.com/*
  10. // @include https://mobile.twitter.com/*
  11. // @include http://www.crowy.net/*
  12. // @include http://twipple.jp/*
  13. // @include https://tweetdeck.twitter.com/*
  14. // @version 1.7.3
  15. // ==/UserScript==
  16.  
  17. (function() {
  18. function source() { // source code
  19.  
  20. var VERSION = '1.7.3';
  21. var USE_LOCAL_STORAGE_ENABLE = true;
  22. var MAX_URL_CACHE = 400;
  23.  
  24. function isSupportXhr2() { return 'withCredentials' in $.ajaxSettings.xhr(); }
  25. function ajax(arg) {
  26. var done = 0;
  27. var option = {
  28. url: arg.url, data: arg.data, dataType: arg.dataType, type: 'GET', cache: true,
  29. success: function(data, status, xhr) { done++ || arg.success(data, status, xhr); },
  30. error: function(xhr, status, errorThrown) { done++ || arg.error(xhr, status, errorThrown); },
  31. };
  32. if (arg.dataType == 'jsonp') {
  33. setTimeout(function() { option.error(); }, 15 * 1000);
  34. }
  35. $.ajax(option);
  36. }
  37. function loadJQuery() {
  38. var id = 'dy-load-jq';
  39. if (document.getElementById(id)) {
  40. return;
  41. }
  42. var script = document.createElement('script');
  43. script.id = id;
  44. script.type = 'text/javascript';
  45. script.src = '//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
  46. document.body.appendChild(script);
  47. }
  48.  
  49. function main(EXT_SERV_HOST_URL) { // main logic
  50.  
  51. var STORE_PREFIX = 'inlineThumbnail.';
  52. var EXPANDED_URL_CACHE_KEY = STORE_PREFIX + 'expandedUrls';
  53. var THUMBNAIL_URL_CACHE_KEY = STORE_PREFIX + 'thumbnailUrls';
  54. var EXT_SERV_ENDS = (function(base) {
  55. var ret = {
  56. expandurl: 'expandurl',
  57. amazon: 'amazon',
  58. flickr: 'flickr',
  59. nicovideo: 'nicovideo',
  60. videosurf: 'videosurf',
  61. tinami: 'tinami',
  62. ustream: 'ustream',
  63. tumblr: 'tumblr',
  64. rakuten: 'rakuten',
  65. ogmedia: 'ogmedia',
  66. myspacevideo: 'myspacevideo',
  67. pictwitter: 'pictwitter',
  68. itunes: 'itunes',
  69. loctouch: 'loctouch',
  70. i500px: '500px',
  71. facebook: 'facebook',
  72. twil: 'proxy/twil'
  73. };
  74. $.each(ret, function(key, val) { ret[key] = base + '/' + val; });
  75. return ret;
  76. })(EXT_SERV_HOST_URL);
  77.  
  78. var HTTPS_SUPPORT_SITE_REGEX = (function() {
  79. var fragment = (
  80. 'pbs.twimg.com twitpic.com img.ly yfrog.com p.twipple.jp farm\\d.static.flickr.com miil.me' + ' ' +
  81. ''
  82. ).replace(/\./g,'\\.').replace(/ /g, '|');
  83. return new RegExp('^https?://(?:' + fragment + ')/');
  84. })();
  85. function modPrt(origUrl) {
  86. if (HTTPS_SUPPORT_SITE_REGEX.test(origUrl)) {
  87. return origUrl.replace(/^https?:/, '');
  88. }
  89. return origUrl;
  90. }
  91.  
  92. var ACCESS_CONTROL_SUPPORT_DOMAIN_REGEX = new RegExp('^' + EXT_SERV_HOST_URL + '/');
  93. var SUPPORT_XHR2_CROSS_DOMAIN = isSupportXhr2();
  94.  
  95. // Utils
  96. function $E(tagName, attributes) {
  97. var e = $(document.createElement(tagName));
  98. attributes && e.attr(attributes);
  99. return e;
  100. }
  101.  
  102. function callWebApi(endpoint, sendData, complete) {
  103. var dataType = SUPPORT_XHR2_CROSS_DOMAIN && ACCESS_CONTROL_SUPPORT_DOMAIN_REGEX.test(endpoint) ? 'json' : 'jsonp';
  104. ajax({
  105. url: endpoint, data: sendData, dataType: dataType,
  106. success: function(data, status, xhr) {
  107. complete(data, xhr && xhr.status);
  108. },
  109. error: function(xhr, status, errorThrown) {
  110. complete(null, xhr && xhr.status);
  111. }
  112. });
  113. }
  114.  
  115. function baseDecode(letters, snipcode) {
  116. var ret = 0;
  117. for (var i = snipcode.length, m = 1; i; i--, m *= letters.length) {
  118. ret += letters.indexOf(snipcode.charAt(i-1)) * m;
  119. }
  120. return ret;
  121. }
  122. function base58decode(snipcode) {
  123. return baseDecode('123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ', snipcode);
  124. }
  125. function base36decode(snipcode) {
  126. return baseDecode('0123456789abcdefghijklmnopqrstuvwxyz', snipcode);
  127. }
  128.  
  129. function unique(array, prop) {
  130. if (array == null) {
  131. return null;
  132. }
  133. var uniqueSet = {};
  134. return $.grep(array, function(value) {
  135. var key = value[prop];
  136. var result = !(key in uniqueSet);
  137. if (result) {
  138. uniqueSet[key] = true;
  139. }
  140. return result;
  141. });
  142. }
  143. function objectToArrayWithKey(obj) {
  144. var arry = [];
  145. for (var k in obj) {
  146. arry.push([k, obj[k]]);
  147. }
  148. return arry;
  149. }
  150. // /Utils
  151.  
  152. // Cache
  153. var Cache = (function() {
  154. var storage = null;
  155. if (USE_LOCAL_STORAGE_ENABLE && window.localStorage) {
  156. storage = localStorage;
  157. } else if (window.sessionStorage) {
  158. storage = sessionStorage;
  159. }
  160.  
  161. function constructor(cacheSizeMax, storeKey) {
  162. this.cacheSizeMax = cacheSizeMax;
  163. this.storeKey = storeKey;
  164. this.interval = 20 * 1000;
  165. this.init();
  166. }
  167.  
  168. function genCache() {
  169. return { version: VERSION, size: 0 };
  170. }
  171.  
  172. constructor.prototype = {
  173. init: function() {
  174. this.modified = false;
  175. this.caches = [];
  176. if (storage && this.storeKey) {
  177. var json = storage[this.storeKey];
  178. if (json) {
  179. var cache = JSON.parse(json);
  180. if (cache.version == VERSION) {
  181. this.caches.push(cache);
  182. }
  183. }
  184. var self = this;
  185. setInterval(function() {
  186. if (self.modified) {
  187. storage[self.storeKey] = JSON.stringify(self.cache());
  188. self.modified = false;
  189. }
  190. }, this.interval);
  191. }
  192. if (this.caches.length == 0) {
  193. this.caches.push(genCache());
  194. }
  195. },
  196. get: function(key) {
  197. for(var i = 0; i < this.caches.length; i++) {
  198. var val = this.caches[i][key];
  199. if (val) {
  200. return val;
  201. }
  202. }
  203. return null;
  204. },
  205. put: function(key, value) {
  206. this.shift();
  207. var cache = this.cache();
  208. if (!(key in cache)) {
  209. cache.size += 1;
  210. }
  211. cache[key] = value;
  212. this.modified = true;
  213. },
  214. shift: function() {
  215. if (this.cache().size > this.cacheSizeMax) {
  216. if (this.caches.unshift(genCache()) > 3) {
  217. this.caches.pop();
  218. }
  219. this.modified = true;
  220. }
  221. },
  222. cache: function() { return this.caches[0]; }
  223. };
  224. return constructor;
  225. })();
  226.  
  227. var ExpandedUrlCache = new Cache(MAX_URL_CACHE, EXPANDED_URL_CACHE_KEY);
  228. ExpandedUrlCache.put_orig = ExpandedUrlCache.put;
  229. ExpandedUrlCache.put = function(key, value) {
  230. if (key == value) {
  231. return;
  232. }
  233. this.put_orig(key, value);
  234. };
  235. var ThumbnailUrlCache = new Cache(MAX_URL_CACHE, THUMBNAIL_URL_CACHE_KEY);
  236. // /Cache
  237.  
  238. // WebService
  239. function expandShortUrl(shortUrl, complete) {
  240. var cached = ExpandedUrlCache.get(shortUrl);
  241. if (cached) {
  242. complete(cached);
  243. return;
  244. }
  245. callWebApi(EXT_SERV_ENDS.expandurl, { url: shortUrl }, function(data, statusCode) {
  246. var longUrl = null;
  247. if (data) {
  248. longUrl = data['long-url'];
  249. ExpandedUrlCache.put(shortUrl, longUrl);
  250. }
  251. complete(longUrl);
  252. });
  253. }
  254.  
  255. var twitcasting = {
  256. fetchUserImage: function(userId, complete) {
  257. callWebApi('http://api.twitcasting.tv/api/userstatus', { user: userId }, function(data) {
  258. complete(data && data.image, true);
  259. });
  260. },
  261. fetchMovieThumbnail: function(movieId, userId, complete) {
  262. var self = this;
  263. callWebApi('http://api.twitcasting.tv/api/moviestatus', { movieid: movieId }, function(movieData) {
  264. if (movieData) {
  265. if (movieData.thumbnailsmall) {
  266. complete(movieData.thumbnailsmall, true);
  267. } else {
  268. self.fetchUserImage(userId, complete);
  269. }
  270. } else {
  271. complete();
  272. }
  273. });
  274. },
  275. fetchLiveThumbnail: function(userId, complete) {
  276. var completeNoCache = function(thumbUrl) { complete(thumbUrl); }; // ignore cache
  277. var self = this;
  278. callWebApi('http://api.twitcasting.tv/api/livestatus', { user: userId }, function(liveData) {
  279. if (liveData) {
  280. if (liveData.islive) {
  281. completeNoCache('http://twitcasting.tv/' + userId + '/thumbstream/liveshot-1');
  282. } else if (liveData.movieid) {
  283. self.fetchMovieThumbnail(liveData.movieid, userId, completeNoCache);
  284. } else {
  285. self.fetchUserImage(userId, completeNoCache);
  286. }
  287. } else {
  288. completeNoCache();
  289. }
  290. });
  291. }
  292. };
  293.  
  294. function fetchUstreamThumbnail(subject, uid, complete) {
  295. callWebApi('http://api.ustream.tv/json/' + subject + '/' + uid + '/getValueOf/imageUrl', {}, function(data) {
  296. complete(data && data.small, true);
  297. });
  298. }
  299.  
  300. function resolvOgmediaDefault(matched, complete) {
  301. callWebApi(EXT_SERV_ENDS.ogmedia, { url: matched[0] }, function(data) {
  302. complete(data && data.url, true);
  303. });
  304. }
  305. // /WebService
  306.  
  307. // ShortUrlRegexs
  308. var SHORT_URL_REGEX = (function() {
  309. var fragment = (
  310. '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' + ' ' +
  311. 'cot.ag p.tl amzn.to dlvr.it goo.gl moi.st dailybooth.com/u tinyurl.com sgp.cm pbckt.com' + ' ' +
  312. 'fon.gs mysp.ac itun.es is.gd v.gd r.sm3.jp tou.ch po.st post.ly pocket.co'
  313. ).replace(/\./g,'\\.').replace(/ /g, '|');
  314. return new RegExp('^http://(?:' + fragment + ')/[\\w\\-:\\.]+');
  315. })();
  316. // /ShortUrlRegexs
  317.  
  318. // ThumbnailResolvers
  319. var THUMB_RESOLVERS = {
  320. allimg: {
  321. priority: -1,
  322. regex: /^https?:\/\/[^?]+?\.(?:jpe?g|png|gif|bmp|JPE?G|PNG|GIF|BMP)(?=\?|$)/,
  323. resolv: function(matched, complete) {
  324. complete(matched[0]);
  325. }
  326. },
  327. twitpic: {
  328. priority: 2,
  329. regex: /^http:\/\/twitpic\.com\/(\w+)/,
  330. regexThumb: /^http:\/\/twitpic\.com\/show\/thumb\/\w+/,
  331. resolv: function(matched, complete) {
  332. complete('http://twitpic.com/show/thumb/' + matched[1]);
  333. }
  334. },
  335. imgly: {
  336. regex: /^http:\/\/img\.ly\/(\w+)/,
  337. regexThumb: /^http:\/\/img\.ly\/show\/thumb\/\w+/,
  338. resolv: function(matched, complete) {
  339. complete('http://img.ly/show/thumb/' + matched[1]);
  340. }
  341. },
  342. twitgoo: {
  343. regex: /^http:\/\/twitgoo\.com\/(\w+)/,
  344. regexThumb: /^http:\/\/twitgoo\.com\/show\/thumb\/\w+/,
  345. resolv: function(matched, complete) {
  346. complete('http://twitgoo.com/show/thumb/' + matched[1]);
  347. }
  348. },
  349. picim: {
  350. regex: /^http:\/\/pic\.im\/(\w+)/,
  351. regexThumb: /^http:\/\/pic\.im\/website\/thumbnail\/\w+/,
  352. resolv: function(matched, complete) {
  353. complete('http://pic.im/website/thumbnail/' + matched[1]);
  354. }
  355. },
  356. youtube: {
  357. regex: /^(?:http:\/\/www\.youtube\.com\/watch\/?\?v=([\w\-]+))|(?:http:\/\/youtu\.be\/([\w\-]+))/,
  358. resolv: function(matched, complete) {
  359. var id = matched[2] || matched[1];
  360. complete('http://i.ytimg.com/vi/' + id + '/default.jpg');
  361. }
  362. },
  363. imgur: {
  364. regex: /^http:\/\/imgur\.com\/(?:r\/pics\/)?(\w+)(?!\/)/,
  365. resolv: function(matched, complete) {
  366. complete('http://i.imgur.com/' + matched[1] + 't.jpg');
  367. }
  368. },
  369. owly: {
  370. regex: /^http:\/\/ow\.ly\/i\/(\w+)/,
  371. resolv: function(matched, complete) {
  372. complete('http://static.ow.ly/photos/thumb/' + matched[1] + '.jpg');
  373. }
  374. },
  375. movapic: {
  376. regex: /^http:\/\/movapic\.com\/pic\/(\w+)/,
  377. resolv: function(matched, complete) {
  378. complete('http://image.movapic.com/pic/s_' + matched[1] + '.jpeg');
  379. }
  380. },
  381. hatena: {
  382. regex: /^http:\/\/f\.hatena\.ne\.jp\/([\w\-]+)\/(\d+)/,
  383. resolv: function(matched, complete) {
  384. 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');
  385. }
  386. },
  387. moby: {
  388. regex: /^http:\/\/moby\.to\/(\w+)/,
  389. resolv: function(matched, complete) {
  390. complete(matched[0] + ':square');
  391. }
  392. },
  393. yfrog: {
  394. regex: /^http:\/\/yfrog\.com\/\w+/,
  395. resolv: function(matched, complete) {
  396. complete(matched[0] + ':small');
  397. }
  398. },
  399. plixi: {
  400. regex: /^http:\/\/plixi\.com\/p\/(\w+)/,
  401. regexThumb: /^http:\/\/api\.plixi\.com\/api\/TPAPI\.svc\/imagefromurl\?size=thumbnail&url=http:\/\/plixi\.com\/p\/\w+/,
  402. resolv: function(matched, complete) {
  403. complete('http://api.plixi.com/api/TPAPI.svc/imagefromurl?size=thumbnail&url=' + matched[0]);
  404. }
  405. },
  406. nicovideo: {
  407. regex: /^(?:http:\/\/www\.nicovideo\.jp\/watch\/(?:sm|nm)(\d+))|(?:http:\/\/nico\.ms\/(?:sm|nm)(\d+))/,
  408. regexThumb: /^http:\/\/tn-skr\d?\.smilevideo\.jp\/smile\?i=\d+/,
  409. resolv: function(matched, complete) {
  410. var id = matched[2] || matched[1];
  411. complete('http://tn-skr.smilevideo.jp/smile?i=' + id);
  412. }
  413. },
  414. nicovideoapi: {
  415. regex: /^(?:http:\/\/(?:www|live)\.nicovideo\.jp\/(?:watch|gate)\/((?:lv)?\d+))|(?:http:\/\/nico\.ms\/((?:lv)?\d+))/,
  416. resolv: function(matched, complete) {
  417. var id = matched[2] || matched[1];
  418. callWebApi(EXT_SERV_ENDS.nicovideo, { id: id }, function(data) {
  419. complete(data && data.url, true);
  420. });
  421. }
  422. },
  423. nicoseiga: {
  424. regex: /^(?:http:\/\/seiga\.nicovideo\.jp\/seiga\/im(\d+))|(?:http:\/\/nico\.ms\/im(\d+))/,
  425. regexThumb: /^http:\/\/lohas\.nicoseiga\.jp\/thumb\/\d+[qi]/,
  426. resolv: function(matched, complete) {
  427. var id = matched[2] || matched[1];
  428. complete('http://lohas.nicoseiga.jp/thumb/' + id + 'q');
  429. }
  430. },
  431. nicocomu: {
  432. regex: /^http:\/\/com\.nicovideo\.jp\/community\/co(\d+)/,
  433. resolv: function(matched, complete) {
  434. complete('http://icon.nimg.jp/community/co' + matched[1] + '.jpg');
  435. }
  436. },
  437. twipple: {
  438. regex: /^http:\/\/p\.twipple\.jp\/(\w+)$/,
  439. regexThumb: /^http:\/\/p\.twpl\.jp\/show\/thumb\/\w+/,
  440. resolv: function(matched, complete) {
  441. complete('http://p.twpl.jp/show/thumb/' + matched[1]);
  442. }
  443. },
  444. photozou: {
  445. regex: /^http:\/\/photozou\.jp\/photo\/show\/\d+\/(\d+)/,
  446. regexThumb: /^http:\/\/photozou\.jp\/p\/thumb\/\d+/,
  447. resolv: function(matched, complete) {
  448. complete('http://photozou.jp/p/thumb/' + matched[1]);
  449. }
  450. },
  451. ustream: {
  452. regex: /^http:\/\/(?:www\.)?ustream\.tv\/(channel|recorded)\/(?:id\/)?([\w\-%]+)/,
  453. subjects: { 'channel': 'channel', 'recorded': 'video' },
  454. resolv: function(matched, complete) {
  455. fetchUstreamThumbnail(this.subjects[matched[1]], matched[2], complete);
  456. }
  457. },
  458. lockerz: {
  459. regex: /^http:\/\/lockerz\.com\/s\/\d+/,
  460. regexThumb: /^http:\/\/api\.plixi\.com\/api\/tpapi\.svc\/imagefromurl\?size=thumbnail&url=http:\/\/lockerz\.com\/s\/\d+/,
  461. resolv: function(matched, complete) {
  462. complete('http://api.plixi.com/api/tpapi.svc/imagefromurl?size=thumbnail&url=' + matched[0]);
  463. }
  464. },
  465. jcomi: {
  466. regex: /^http:\/\/(?:www\.|vw\.)?j-comi\.jp\/(?:book|murasame)\/(?:comic|detail|view)\/\d+/,
  467. resolv: resolvOgmediaDefault
  468. },
  469. picplz: {
  470. regex: /^http:\/\/picplz\.com\/(?:user\/\w+\/pic\/(\w+)|(\w+))/,
  471. resolv: function(matched, complete) {
  472. var param = {};
  473. if (matched[1]) {
  474. param.longurl_id = matched[1];
  475. } else {
  476. param.shorturl_id = matched[2];
  477. }
  478. callWebApi('http://api.picplz.com/api/v2/pic.json', param, function(data) {
  479. var imgUrl = null;
  480. if (data && data.result == 'ok') {
  481. imgUrl = data.value.pics[0].pic_files['100sh'].img_url;
  482. };
  483. complete(imgUrl, true);
  484. });
  485. }
  486. },
  487. kabegami: {
  488. regex: /^http:\/\/www\.kabegami\.com\/[\w\-]+\/\w+\/show\/id\/(PHOT(\d{10})(\w\w)(\w\w)\w\w)/,
  489. resolv: function(matched, complete) {
  490. complete(['http://www.kabegami.com/content_image/phot', matched[2], matched[3], matched[4], matched[1]].join('/') + '_s90.jpg');
  491. }
  492. },
  493. instagram: {
  494. regex: /^http:\/\/(?:instagr\.am|instagram\.com)\/p\/[\w\-]+/,
  495. resolv: function(matched, complete) {
  496. complete(matched[0] + '/media/?size=t');
  497. }
  498. },
  499. mypix: {
  500. regex: /^http:\/\/www\.mypix\.jp\/app\.php\/picture\/\d+/,
  501. resolv: function(matched, complete) {
  502. complete(matched[0] + '/thumbx.jpg');
  503. }
  504. },
  505. fotolog: {
  506. regex: /^http:\/\/fotolog\.cc\/(\w+)/,
  507. regexThumb: /^http:\/\/fotolog\.cc\/image\/\w+\/mini/,
  508. resolv: function(matched, complete) {
  509. complete('http://fotolog.cc/image/' + matched[1] + '/mini');
  510. }
  511. },
  512. vimeo: {
  513. regex: /^http:\/\/(?:www\.)?vimeo\.com\/(\d+)/,
  514. resolv: function(matched, complete) {
  515. callWebApi('http://vimeo.com/api/v2/video/' + matched[1] + '.json', {}, function(data) {
  516. complete(data && data[0].thumbnail_small, true);
  517. });
  518. }
  519. },
  520. dailybooth: {
  521. regex: /^http:\/\/dailybooth\.com\/[\w\-]{2,}\/(\d+)/,
  522. resolv: function(matched, complete) {
  523. callWebApi('http://api.dailybooth.com/v2/pictures/' + matched[1] + '.json', {}, function(data) {
  524. complete(data && data.urls.small, true);
  525. });
  526. }
  527. },
  528. twitcasting: {
  529. regex: /^http:\/\/twitcasting\.tv\/([\w\-]+)\/movie\/(\d+)/,
  530. resolv: function(matched, complete) {
  531. var userId = matched[1];
  532. var movieId = matched[2];
  533. twitcasting.fetchMovieThumbnail(movieId, userId, complete);
  534. }
  535. },
  536. twitcastinglive: {
  537. regex: /^http:\/\/twitcasting\.tv\/([\w\-]+)\/?$/,
  538. resolv: function(matched, complete) {
  539. var userId = matched[1];
  540. twitcasting.fetchLiveThumbnail(userId, complete);
  541. }
  542. },
  543. metacafe: {
  544. regex: /^http:\/\/www\.metacafe\.com\/(?:w|watch)\/([\w\-]+)/,
  545. resolv: function(matched, complete) {
  546. complete('http://www.metacafe.com/thumb/' + matched[1] + '.jpg');
  547. }
  548. },
  549. dailymotion: {
  550. regex: /^http:\/\/(?:www|touch)\.dailymotion\.com\/(?:#\/)?video\/([\w\-]+)/,
  551. resolv: function(matched, complete) {
  552. callWebApi('https://api.dailymotion.com/video/' + matched[1], { fields: 'thumbnail_medium_url' }, function(data) {
  553. complete(data && data.thumbnail_medium_url, true);
  554. });
  555. }
  556. },
  557. stickamjpprof: {
  558. regex: /^http:\/\/(?:www\.stickam\.jp\/profile|stick\.am\/p)\/(\w+)/,
  559. resolv: function(matched, complete) {
  560. callWebApi('http://api.stickam.jp/api/user/' + matched[1] + '/profile', { mime: 'json' }, function(data) {
  561. complete(data && data.profile_image, true);
  562. });
  563. }
  564. },
  565. stickamjpmedia: {
  566. regex: /^http:\/\/www\.stickam\.jp\/video\/(\d+)/,
  567. resolv: function(matched, complete) {
  568. callWebApi('http://api.stickam.jp/api/media/' + matched[1], { mime: 'json' }, function(data) {
  569. complete(data && data.media && data.media.thumb, true);
  570. });
  571. }
  572. },
  573. photobucket: {
  574. regex: /^http:\/\/[is]\d+\.photobucket\.com\/albums\/.+/,
  575. resolv: function(matched, complete) {
  576. callWebApi('http://photobucket.com/oembed', { url: matched[0] }, function(data) {
  577. complete(data && data.thumbnail_url, true);
  578. });
  579. }
  580. },
  581. pixiv: {
  582. regex: /^http:\/\/(?:www\.pixiv\.net\/member_illust\.php\/?\?(?:mode=medium&)?illust_id=|p\.tl\/i\/)(\d+)/,
  583. resolv: function (matched, complete) {
  584. callWebApi(EXT_SERV_ENDS.ogmedia, { url: 'http://www.pixiv.net/member_illust.php?mode=medium&illust_id=' + matched[1] }, function(data) {
  585. complete(data && data.url, true);
  586. });
  587. }
  588. },
  589. flickr: {
  590. regex: /^http:\/\/(?:www\.flickr\.com\/photos\/[\w@\-]+\/(\d+))|(?:flic\.kr\/p\/(\w+))/,
  591. resolv: function(matched, complete) {
  592. var id = matched[2] ? base58decode(matched[2]) : matched[1];
  593. callWebApi(EXT_SERV_ENDS.flickr, { photo_id: id }, function(data) {
  594. complete(data && data.url, true);
  595. });
  596. }
  597. },
  598. videosurf: {
  599. regex: /^http:\/\/www\.videosurf\.com\/video\/-?(?:[a-zA-Z0-9%]+-)*(\d+)\b/,
  600. resolv: function(matched, complete) {
  601. callWebApi(EXT_SERV_ENDS.videosurf, { id: matched[1] }, function(data) {
  602. complete(data && data.url, true);
  603. });
  604. }
  605. },
  606. tinami: {
  607. regex: /^http:\/\/(?:www\.tinami\.com\/view\/(\w+)|tinami\.jp\/(\w+))/,
  608. resolv: function(matched, complete) {
  609. var id = matched[2] ? base36decode(matched[2]) : matched[1];
  610. callWebApi(EXT_SERV_ENDS.tinami, { id: id }, function(data) {
  611. complete(data && data.url, true);
  612. });
  613. }
  614. },
  615. akibablog: {
  616. regex: /^http:\/\/blog\.livedoor\.jp\/geek\/archives\/\d+\.html/,
  617. resolv: resolvOgmediaDefault
  618. },
  619. photobucketmedia: {
  620. regex: /^http:\/\/media\.photobucket\.com\/.+/,
  621. resolv: resolvOgmediaDefault
  622. },
  623. ustreamsp: {
  624. regex: /^http:\/\/(?:www\.)?ustream\.tv\/[\w\-%]+(?=\/?$)/,
  625. resolv: function(matched, complete) {
  626. callWebApi(EXT_SERV_ENDS.ustream, { url: matched[0] }, function(data) {
  627. if (data) {
  628. fetchUstreamThumbnail('channel', data.channelId, complete);
  629. } else {
  630. complete();
  631. }
  632. });
  633. }
  634. },
  635. tumblr: {
  636. regex: /^http:\/\/([\w\-]+\.tumblr\.com)\/post\/(\d+)/,
  637. resolv: function(matched, complete) {
  638. callWebApi(EXT_SERV_ENDS.tumblr, { base_hostname: matched[1], post_id: matched[2] }, function(data) {
  639. complete(data && data.url, true);
  640. });
  641. }
  642. },
  643. tumblrs: {
  644. regex: /^http:\/\/(?:tumblr\.com|tmblr\.co)\/[\w\-]+/,
  645. resolv: function(matched, complete) {
  646. expandShortUrl(matched[0], function(longUrl) {
  647. var regex = /^http:\/\/((?:\.?[\w\-]+)+)\/post\/(\d+)/;
  648. if (regex.test(longUrl)) {
  649. THUMB_RESOLVERS.tumblr.resolv(longUrl.match(regex), complete);
  650. } else {
  651. complete();
  652. }
  653. });
  654. }
  655. },
  656. rakutenbooks: {
  657. regex: /^http:\/\/books\.rakuten\.co\.jp\/rb\/[\w%\-]+\-(\d+)\//,
  658. resolv: function(matched, complete) {
  659. callWebApi(EXT_SERV_ENDS.rakuten, { type: 'book', id: matched[1] }, function(data) {
  660. complete(data && data.url, true);
  661. });
  662. }
  663. },
  664. rakutenitem: {
  665. regex: /^http:\/\/item\.rakuten\.co\.jp\/([\w\-]+\/[\w\-]+)/,
  666. resolv: function(matched, complete) {
  667. callWebApi(EXT_SERV_ENDS.rakuten, { type: 'item', id: matched[1] }, function(data) {
  668. complete(data && data.url, true);
  669. });
  670. }
  671. },
  672. twil: {
  673. regex: /^http:\/\/shlink\.st\/[\w\-]+/,
  674. resolv: function(matched, complete) {
  675. callWebApi(EXT_SERV_ENDS.twil, { url: matched[0] }, function(data) {
  676. complete(data && data.thumbnail_url, true);
  677. });
  678. }
  679. },
  680. twitrpix: {
  681. regex: /^http:\/\/twitrpix\.com\/(\w+)/,
  682. regexThumb: /^http:\/\/img\.twitrpix\.com\/thumb\/\w+/,
  683. resolv: function(matched, complete) {
  684. complete('http://img.twitrpix.com/thumb/' + matched[1]);
  685. }
  686. },
  687. pikchur: {
  688. regex: /^http:\/\/(?:pikchur\.com|pk\.gd)\/(\w+)/,
  689. resolv: function(matched, complete) {
  690. complete(' http://img.pikchur.com/pic_' + matched[1] + '_s.jpg');
  691. }
  692. },
  693. twitxr: {
  694. regex: /^http:\/\/twitxr\.com\/(\w+)\/updates\/(\d+)/,
  695. regexThumb: /^http:\/\/twitxr\.com\/image\/\d+\/th/,
  696. resolv: function(matched, complete) {
  697. complete('http://twitxr.com/image/' + matched[2] + '/th');
  698. }
  699. },
  700. myspacevideo: {
  701. regex: /^http:\/\/(?:www\.)?myspace\.com\/video\/(?:(vid|rid)|[\w-]+\/[\w-]+)\/(\d+)/,
  702. resolv: function(matched, complete) {
  703. var id = (matched[1] || 'vid') + '/' + matched[2];
  704. callWebApi(EXT_SERV_ENDS.myspacevideo, { id: id }, function(data) {
  705. complete(data && data.url, true);
  706. });
  707. }
  708. },
  709. myspacevideovids: {
  710. regex: /^http:\/\/(?:vids\.|www\.)?myspace\.com\/index\.cfm\?fuseaction=vids\.individual&videoid=(\d+)/,
  711. resolv: function(matched, complete) {
  712. var id = 'vid/' + matched[1];
  713. callWebApi(EXT_SERV_ENDS.myspacevideo, { id: id }, function(data) {
  714. complete(data && data.url, true);
  715. });
  716. }
  717. },
  718. twitvideo: {
  719. regex: /^http:\/\/twitvideo\.jp\/(\w+)/,
  720. regexThumb: /^http:\/\/twitvideo\.jp\/img\/thumb\/\w+/,
  721. resolv: function(matched, complete) {
  722. complete('http://twitvideo.jp/img/thumb/' + matched[1]);
  723. }
  724. },
  725. mycom: {
  726. regex: /^http:\/\/(?:if\.|s\.)?journal\.mycom\.co\.jp\/\w+\/\d+\/\d+\/\d+\/\w+\//,
  727. resolv: function(matched, complete) {
  728. complete(matched[0] + 'index.top.jpg');
  729. }
  730. },
  731. twitvid: {
  732. regex: /^http:\/\/(?:www\.)?twitvid\.com\/(?!videos)(\w+)/,
  733. resolv: function(matched, complete) {
  734. var id = matched[1];
  735. complete(['http://llphotos.twitvid.com/twitvidthumbnails', id.charAt(0), id.charAt(1), id.charAt(2), id].join('/') + '_med.jpg');
  736. }
  737. },
  738. pckles: {
  739. regex: /^http:\/\/(?:pckl\.es|pckles\.com)\/\w+\/\w+/,
  740. resolv: function(matched, complete) {
  741. complete(matched[0] + '.thumb.jpg');
  742. }
  743. },
  744. itunes: {
  745. regex: /^http:\/\/(?:c\.)?itunes\.apple\.com\/.*\/id\w+(?:\?i=\d+)?/,
  746. resolv: function(matched, complete) {
  747. callWebApi(EXT_SERV_ENDS.itunes, { url: matched[0] }, function(data) {
  748. complete(data && data.url, true);
  749. });
  750. }
  751. },
  752. loctouch: {
  753. regex: /^http:\/\/tou\.ch\/spot\/\d+\/(\w+)\/(\d+)/,
  754. resolv: function(matched, complete) {
  755. callWebApi(EXT_SERV_ENDS.loctouch, { type: matched[1], id: matched[2] }, function(data) {
  756. complete(data && data.url, true);
  757. });
  758. }
  759. },
  760. miil: {
  761. regex: /^http:\/\/miil\.me\/p\/(\w+)/,
  762. resolv: function(matched, complete) {
  763. complete(matched[0] + '.jpeg?size=150');
  764. }
  765. },
  766. i500px: {
  767. regex: /^https?:\/\/(?:www\.)?500px\.com\/photo\/(\d+)/,
  768. resolv: function(matched, complete) {
  769. callWebApi(EXT_SERV_ENDS.i500px, { id: matched[1] }, function(data) {
  770. complete(data && data.url, true);
  771. });
  772. }
  773. },
  774. hulu: {
  775. regex: /^http:\/\/(?:www\.hulu\.com\/watch|hulu\.com\/w)\/.*/,
  776. resolv: function(matched, complete) {
  777. callWebApi('http://www.hulu.com/api/oembed', { url: matched[0] }, function(data) {
  778. complete(data && data.thumbnail_url, true);
  779. });
  780. }
  781. },
  782. facebook: {
  783. regex: /^https?:\/\/www\.facebook\.com\/photo\.php\?fbid=(\w+)/,
  784. resolv: function(matched, complete) {
  785. callWebApi(EXT_SERV_ENDS.facebook, { id: matched[1] }, function(data) {
  786. complete(data && data.url, true);
  787. });
  788. }
  789. },
  790. immio: {
  791. regex: /^http:\/\/imm\.io\/\w+/,
  792. resolv: resolvOgmediaDefault
  793. },
  794. comicdash: {
  795. regex: /^http:\/\/ckworks\.jp\/comicdash\/series\/\d+/,
  796. resolv: resolvOgmediaDefault
  797. },
  798. gyazo: {
  799. regex: /^http:\/\/gyazo\.com\/\w+/,
  800. resolv: function(matched, complete) {
  801. complete(matched[0] + '.png');
  802. }
  803. },
  804. viame: {
  805. regex: /^http:\/\/via\.me\/-\w+/,
  806. resolv: resolvOgmediaDefault
  807. },
  808. posterous: {
  809. regex: /^http:\/\/[\w\-]+\.posterous\.com\/[\w\-]+/,
  810. resolv: resolvOgmediaDefault
  811. },
  812. bookmetercmt: {
  813. regex: /^http:\/\/book\.akahoshitakuya\.com\/cmt\/\w+/,
  814. resolv: resolvOgmediaDefault
  815. },
  816. booklogp: {
  817. regex: /^http:\/\/p\.booklog\.jp\/book\/\d+/,
  818. resolv: function(matched, complete) {
  819. callWebApi(EXT_SERV_ENDS.ogmedia, { url: matched[0] }, function(data) {
  820. var imgUrl = null;
  821. if (data && data.url) {
  822. imgUrl = data.url.replace(/_s\.jpg$/, '_m.jpg');
  823. }
  824. complete(imgUrl, true);
  825. });
  826. }
  827. },
  828. booklogitem: {
  829. regex: /^http:\/\/booklog\.jp\/item\/3\/\d+/,
  830. resolv: resolvOgmediaDefault
  831. },
  832. mlkshk: {
  833. regex: /^http:\/\/mlkshk\.com\/\w\/(\w+)/,
  834. resolv: function(matched, complete) {
  835. complete('http://mlkshk.com/r/' + matched[1]);
  836. }
  837. },
  838. twitdraw: {
  839. regex: /^http:\/\/twitdraw\.com\/(\w+)/,
  840. resolv: function(matched, complete) {
  841. complete('http://td-images.s3.amazonaws.com/' + matched[1] + '.png');
  842. }
  843. },
  844. tegaki: {
  845. regex: /^http:\/\/tegaki\.pipa\.jp\/\d+\/(\d+)\.html/,
  846. resolv: function(matched, complete) {
  847. complete('http://tegaki.pipa.jp/CGetBlogImgS.jsp?TD=' + matched[1]);
  848. }
  849. },
  850. gizmodo: {
  851. regex: /^http:\/\/(?:www|us)\.gizmodo\.(?:jp|co\.uk|de|com)\/\d+\/(?:\d+\/)?[\w\.\-]+/,
  852. resolv: resolvOgmediaDefault
  853. },
  854. piapro: {
  855. regex: /^http:\/\/piapro\.jp\/t\/[\w\-]+/,
  856. resolv: resolvOgmediaDefault
  857. },
  858. tweetvite: {
  859. regex: /^http:\/\/(?:tweetvite\.com\/event|twvt\.us)\/([\w\-]+)/,
  860. resolv: function resolvOgmediaDefault(matched, complete) {
  861. var url = 'http://tweetvite.com/event/' + matched[1];
  862. callWebApi(EXT_SERV_ENDS.ogmedia, { url: url }, function(data) {
  863. complete(data && data.url, true);
  864. });
  865. }
  866. },
  867. twitter: {
  868. priority: 1,
  869. regex: /^https?:\/\/twitter\.com\/(?:#!\/)?[\w\-]+\/status\/(\d+)\/photo\/1/,
  870. resolv: function(matched, complete) {
  871. callWebApi(EXT_SERV_ENDS.pictwitter, { id: matched[1] }, function(data) {
  872. complete(data && data.url, true);
  873. });
  874. }
  875. },
  876. eyeem: {
  877. regex: /^http:\/\/www\.eyeem\.com\/p\/\d+/,
  878. resolv: resolvOgmediaDefault
  879. },
  880. vine: {
  881. regex: /^https?:\/\/vine\.co\/v\/\w+/,
  882. resolv: resolvOgmediaDefault
  883. }
  884. };
  885. // amazon
  886. (function(resolvers) {
  887. var fetchAmazonThumbnail = function(sendData, complete) {
  888. callWebApi(EXT_SERV_ENDS.amazon, sendData, function(data) {
  889. complete(data && data.url, true);
  890. });
  891. };
  892. var resolvAmazonThumbnailJpDefault = function(matched, complete) {
  893. fetchAmazonThumbnail({ tld: 'jp', asin: matched[1] }, complete);
  894. };
  895.  
  896. resolvers.amazon = {
  897. 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+))/,
  898. resolv: function(matched, complete) {
  899. fetchAmazonThumbnail({ tld: matched[1], asin: matched[3] || matched[2] }, complete);
  900. }
  901. };
  902. resolvers.bookmeter = {
  903. regex: /^http:\/\/book\.akahoshitakuya\.com\/b\/(\w+)/,
  904. resolv: resolvAmazonThumbnailJpDefault
  905. };
  906. resolvers.mediamarker = {
  907. regex: /^http:\/\/mediamarker\.net\/\w\/[\w\-]+\/\?asin=(\w+)/,
  908. resolv: resolvAmazonThumbnailJpDefault
  909. };
  910. resolvers.booklog = {
  911. regex: /^http:\/\/booklog\.jp\/(?:users\/[\w\-]+\/archives|asin|item)\/1\/(\w+)/,
  912. resolv: resolvAmazonThumbnailJpDefault
  913. };
  914. resolvers.sociallibrary = {
  915. regex: /^http:\/\/www\.sociallibrary\.jp\/entry\/(\w+)/,
  916. resolv: resolvAmazonThumbnailJpDefault
  917. };
  918. })(THUMB_RESOLVERS);
  919. // /amazon
  920.  
  921. // /ThumbnailResolvers
  922.  
  923. // Process
  924. var Process = function(args) {
  925. this.waitCount = 0;
  926. this.results = [];
  927. this.split = args.split;
  928. this.execute = args.execute;
  929. this.complete = args.complete;
  930. this.queue = args.queue;
  931. };
  932. Process.prototype = {
  933. run: function(input) {
  934. var subInputs = this.split(input);
  935. var inputLength = subInputs ? subInputs.length : 0;
  936. this.waitCount = inputLength;
  937. this.internalComplete();
  938. for (var i = 0; i < inputLength; i++) {
  939. this.execute(subInputs[i]);
  940. }
  941. },
  942. emitComplete: function(result) {
  943. this.waitCount--;
  944. if (result) {
  945. this.results.push(result);
  946. }
  947. this.internalComplete();
  948. },
  949. internalComplete: function() {
  950. if (this.waitCount == 0) {
  951. if (this.complete) {
  952. this.complete(this.results);
  953. }
  954. if (this.queue.length != 0) {
  955. this.queue.shift()();
  956. }
  957. }
  958. }
  959. };
  960. // /Process
  961.  
  962. // ExpandThumbnailMainProcess
  963. var thumbResolverWithKeyArry = objectToArrayWithKey(THUMB_RESOLVERS);
  964. thumbResolverWithKeyArry.sort(function(a, b) {
  965. var a_p = 'priority' in a[1] ? a[1].priority : 0;
  966. var b_p = 'priority' in b[1] ? b[1].priority : 0;
  967. if (a_p < b_p) return 1;
  968. if (a_p > b_p) return -1;
  969. return 0;
  970. });
  971. function resolvThumbnailUrl(existsOfficalThumbnails, contentUrl, complete) {
  972. var cached = ThumbnailUrlCache.get(contentUrl);
  973. if (cached) {
  974. complete(cached);
  975. return true;
  976. }
  977. var match = false;
  978. $.each(thumbResolverWithKeyArry, function(index, resolverWithKey) {
  979. var resolver = resolverWithKey[1];
  980. if (resolver.regex.test(contentUrl)) {
  981. if (existsOfficalThumbnails && domainEnv.supportedThumbnails && resolverWithKey[0] in domainEnv.supportedThumbnails) {
  982. return false;
  983. }
  984. resolver.resolv(contentUrl.match(resolver.regex), function(thumbnailUrl, cache) {
  985. if (thumbnailUrl && cache) {
  986. ThumbnailUrlCache.put(contentUrl, thumbnailUrl);
  987. }
  988. complete(thumbnailUrl);
  989. });
  990. match = true;
  991. return false;
  992. } else if (resolver.regexThumb && resolver.regexThumb.test(contentUrl)) {
  993. complete(contentUrl.match(resolver.regexThumb)[0]);
  994. match = true;
  995. return false;
  996. }
  997. });
  998. return match;
  999. }
  1000.  
  1001. function createThumbnailElement(urlEntries) {
  1002. var ul = $E('ul', { 'class': 'ithumb-ul' });
  1003. for (var i = 0; i < urlEntries.length; i++) {
  1004. var urlEntry = urlEntries[i];
  1005. ul.append(
  1006. $E('li', { 'class':'ithumb-li' }).append(
  1007. $E('a', { 'class':'ithumb-a', 'target':'_blank', 'href':urlEntry.url, 'rel':'url' }).append(
  1008. $E('img', { 'src': modPrt(urlEntry.thumbUrl), 'class':'ithumb-img' })
  1009. .error(function() {
  1010. var img = $(this);
  1011. var tryload = img.data('tryload') || 0;
  1012. if (tryload < 2) {
  1013. setTimeout(function() {
  1014. img.data('tryload', tryload + 1).attr('src', img.attr('src'));
  1015. }, 7000);
  1016. } else {
  1017. img.hide();
  1018. }
  1019. })
  1020. )
  1021. )
  1022. );
  1023. }
  1024. var thumb = $E('div', { 'class':'ithumb-container' }).append(ul);
  1025. if (domainEnv.customizeThumbnailElement) {
  1026. domainEnv.customizeThumbnailElement(thumb);
  1027. }
  1028. return thumb;
  1029. }
  1030.  
  1031. function expandThumbnail(contentElement, existsOfficalThumbnails) {
  1032. var urlStack = [];
  1033. var processQueue = [];
  1034.  
  1035. /* <thumbnailExpandProcess> */
  1036. var urlExtractProcess = new Process({
  1037. queue: processQueue,
  1038. split: function(element) {
  1039. return element.find('a').map(function() {
  1040. var anchor = $(this);
  1041. var url = anchor.attr('href');
  1042. return (url && (/^https?:\/\/(?!twitter\.com\/#!\/)/).test(url)) ? anchor : null;
  1043. });
  1044. },
  1045. execute: function(anchor) {
  1046. var urlEntry = { thumbUrl: null, expandCount: 0 };
  1047. var expandedUrl = null;
  1048. if (domainEnv.getExpandedUrl) {
  1049. expandedUrl = domainEnv.getExpandedUrl(anchor);
  1050. }
  1051. urlEntry.url = expandedUrl || anchor.attr('href');
  1052. this.emitComplete(urlEntry);
  1053. },
  1054. complete: function(urlEntries) {
  1055. urlStack = unique(urlEntries, 'url');
  1056. }
  1057. });
  1058.  
  1059. var thumbnailUrlResolveProcess = new Process({
  1060. queue: processQueue,
  1061. split: function(urlEntries) {
  1062. return urlEntries;
  1063. },
  1064. execute: function (urlEntry) {
  1065. var self = this;
  1066. var match = resolvThumbnailUrl(existsOfficalThumbnails, urlEntry.url, function(thumbUrl) {
  1067. urlEntry.thumbUrl = thumbUrl;
  1068. self.emitComplete();
  1069. });
  1070. if (!match) {
  1071. if (urlEntry.expandCount < 5 && SHORT_URL_REGEX.test(urlEntry.url)) {
  1072. expandShortUrl(urlEntry.url, function(longUrl) {
  1073. if (urlEntry.url != longUrl) {
  1074. urlEntry.expandCount += 1;
  1075. urlEntry.url = longUrl;
  1076. self.execute(urlEntry);
  1077. } else {
  1078. self.emitComplete();
  1079. }
  1080. });
  1081. } else {
  1082. self.emitComplete();
  1083. }
  1084. }
  1085. },
  1086. });
  1087. /* </thumbnailExpandProcess> */
  1088.  
  1089. processQueue.push(function() { thumbnailUrlResolveProcess.run(urlStack); });
  1090. processQueue.push(function() {
  1091. var urlEntries = unique(
  1092. $.grep(urlStack, function(val) { return val.thumbUrl; }),
  1093. 'thumbUrl');
  1094. if (urlEntries.length != 0) {
  1095. domainEnv.appendThumbnail(contentElement, createThumbnailElement(urlEntries));
  1096. }
  1097. });
  1098.  
  1099. urlExtractProcess.run(contentElement);
  1100. }
  1101. // /ExpandThumbnailMainProcess
  1102.  
  1103. var CSS_URL_DEFAULT = EXT_SERV_HOST_URL + '/stylesheets/inlinethumbnail/1.1.0/default.css';
  1104. var APPEND_THUMBNAIL_DEFAULT = function(contentElement, thumbnailElement) { contentElement.append(thumbnailElement); };
  1105. var DOMAIN_ENVS = {
  1106. hootsuite: {
  1107. hostname: 'hootsuite.com',
  1108. select: function(context) {
  1109. return $('._baseTweetText:not([expanded-img])', context);
  1110. },
  1111. appendThumbnail: APPEND_THUMBNAIL_DEFAULT,
  1112. cssUrl: CSS_URL_DEFAULT
  1113. },
  1114. twitter: {
  1115. hostname: 'twitter.com',
  1116. select: function(context) {
  1117. return $('.tweet:not(.simple-tweet) > .content > p:not([expanded-img])', context);
  1118. },
  1119. appendThumbnail: APPEND_THUMBNAIL_DEFAULT,
  1120. getExpandedUrl: function(anchor) { return anchor.data('expanded-url'); },
  1121. cssUrl: EXT_SERV_HOST_URL + '/stylesheets/inlinethumbnail/1.6.2/twittercom.css',
  1122. existsOfficalThumbnails: function(contentElement) {
  1123. return contentElement.nextAll('.cards-media-container').length > 0;
  1124. },
  1125. supportedThumbnails: {
  1126. 'twitter' : true
  1127. }
  1128. },
  1129. twitter_mobile: {
  1130. hostname: 'mobile.twitter.com',
  1131. select: function(context) {
  1132. return $('.tweet-text:not([expanded-img])', context);
  1133. },
  1134. appendThumbnail: APPEND_THUMBNAIL_DEFAULT,
  1135. getExpandedUrl: function(anchor) { return anchor.data('url'); },
  1136. cssUrl: CSS_URL_DEFAULT
  1137. },
  1138. crowy: {
  1139. hostname: 'www.crowy.net',
  1140. select: function(context) {
  1141. return $('.message-text:not([expanded-img])', context);
  1142. },
  1143. appendThumbnail: APPEND_THUMBNAIL_DEFAULT,
  1144. cssUrl: CSS_URL_DEFAULT,
  1145. existsOfficalThumbnails: function(contentElement) {
  1146. return contentElement.nextAll('.images').length > 0;
  1147. },
  1148. supportedThumbnails: {
  1149. 'twitter' : true,
  1150. 'twitpic' : true,
  1151. 'youtube' : true,
  1152. 'amazon' : true
  1153. }
  1154. },
  1155. twipple: {
  1156. hostname: 'twipple.jp',
  1157. select: function(context) {
  1158. return $('.tweetBox:not([expanded-img])', context.parentNode);
  1159. },
  1160. appendThumbnail: APPEND_THUMBNAIL_DEFAULT,
  1161. cssUrl: CSS_URL_DEFAULT,
  1162. existsOfficalThumbnails: function(contentElement) {
  1163. return contentElement.find('.thumbBoxImage').length > 0;
  1164. },
  1165. supportedThumbnails: {
  1166. 'twitter' : true,
  1167. 'twitpic' : true,
  1168. 'youtube' : true
  1169. }
  1170. },
  1171. tweetdeck: {
  1172. hostname: 'tweetdeck.twitter.com',
  1173. select: function(context) {
  1174. return $('.tweet-body:not([expanded-img])', context.parentNode);
  1175. },
  1176. appendThumbnail: function(contentElement, thumbnailElement) { contentElement.after(thumbnailElement); },
  1177. getExpandedUrl: function(anchor) { return anchor.data('full-url'); },
  1178. cssUrl: CSS_URL_DEFAULT,
  1179. existsOfficalThumbnails: function(contentElement) {
  1180. return contentElement.children('.media-preview').length > 0;
  1181. },
  1182. supportedThumbnails: {
  1183. 'twitter' : true,
  1184. 'twitpic' : true,
  1185. 'youtube' : true
  1186. }
  1187. }
  1188. };
  1189.  
  1190. var domainEnv = null;
  1191. $.each(DOMAIN_ENVS, function() {
  1192. if (this.hostname == location.hostname) {
  1193. if (this.match) {
  1194. if (this.match()) {
  1195. domainEnv = this;
  1196. return false;
  1197. }
  1198. } else {
  1199. domainEnv = this;
  1200. return false;
  1201. }
  1202. }
  1203. });
  1204.  
  1205. function applyElements(context) {
  1206. domainEnv.select(context).each(function() {
  1207. var contentElement = $(this);
  1208. expandThumbnail(
  1209. contentElement.attr('expanded-img', 'expanded-img'),
  1210. domainEnv.existsOfficalThumbnails && domainEnv.existsOfficalThumbnails(contentElement));
  1211. });
  1212. }
  1213.  
  1214. $(document.getElementsByTagName('head')[0]).append($E('link', { 'rel':'stylesheet', 'type':'text/css', 'media':'screen', 'href':domainEnv.cssUrl }));
  1215.  
  1216. var ApplyQueue = {
  1217. timeoutId: null,
  1218. queue: [],
  1219. apply: function() {
  1220. var targets = this.queue.splice(0, this.queue.length);
  1221. applyElements(targets);
  1222. },
  1223. push: function(elem) {
  1224. if (this.timeoutId) {
  1225. clearTimeout(this.timeoutId);
  1226. this.timeoutId = null;
  1227. }
  1228. this.queue.push(elem);
  1229.  
  1230. var self = this;
  1231. this.timeoutId = setTimeout(function() {
  1232. self.apply();
  1233. }, 1000);
  1234. }
  1235. };
  1236.  
  1237. // initial apply
  1238. ApplyQueue.push(document);
  1239.  
  1240. $(document).bind('DOMNodeInserted', function(e) {
  1241. ApplyQueue.push(e.target);
  1242. });
  1243.  
  1244. } // /main logic
  1245.  
  1246. // load
  1247. (function mainloader(tryCount, loaded) {
  1248. if (tryCount < 30 && !(window.jQuery)) {
  1249. setTimeout(function() { mainloader(tryCount + 1, loaded); }, 60);
  1250. return;
  1251. }
  1252. if (!loaded && !(window.jQuery)) {
  1253. loadJQuery();
  1254. setTimeout(function() { mainloader(0, true); }, 60);
  1255. return;
  1256. }
  1257. var hostname = 'thumbnailurlconv.appspot.com';
  1258. var endpoint = '//' + hostname + '/endpoint';
  1259. var dataType = isSupportXhr2() ? 'json' : 'jsonp';
  1260. ajax({ url: endpoint, dataType: dataType,
  1261. success: function(data) { main('//' + (data ? data.hostname : hostname)); },
  1262. error: function() { main('//' + hostname); }
  1263. });
  1264. })(0);
  1265.  
  1266. } // /source code
  1267.  
  1268. var script = document.createElement('script');
  1269. script.type = 'text/javascript';
  1270. script.innerHTML = '(' + source.toString() + ')();';
  1271. document.body.appendChild(script);
  1272.  
  1273. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址