Greasy Fork is available in English.

Reddit_Mouseover_Popup_Image_Viewer

Shows larger version of thumbnails on reddit.com.

  1. // ==UserScript==
  2. // @id org.userscripts.users.jim50.RedditMouseoverPopupImageViewer
  3. // @name Reddit_Mouseover_Popup_Image_Viewer
  4. // @description Shows larger version of thumbnails on reddit.com.
  5. // @version 2014.3.141
  6. // @author jim50
  7. // @homepage http://userscripts.org/scripts/show/417751
  8. // @icon http://s3.amazonaws.com/uso_ss/icon/417751/large.png?1395216776
  9. // @include http://www.reddit.com/*
  10. // @exclude http*//boards.4chan.org/*/catalog*
  11. //original script by kuehlschrank (http://userscripts.org/scripts/show/109262)
  12. // @namespace Reddit Mouseover Popup Image Viewer
  13. // ==/UserScript==
  14.  
  15. 'use strict';
  16.  
  17. var d = document, wn = window, _ = {}, cfg = loadCfg(), enabled = true, hosts;
  18.  
  19. function loadCfg() {
  20. return fixCfg(GM_getValue('cfg'), true);
  21. }
  22.  
  23. function fixCfg(s, save) {
  24. var cfg, def = {
  25. version: 2,
  26. delay: 500,
  27. thumbsonly: true,
  28. start: 'auto',
  29. zoom: 'context',
  30. halfzoom: true,
  31. center: false,
  32. cursor: false,
  33. killswitch: true,
  34. css: '',
  35. hosts: '',
  36. scale: 1.5
  37. };
  38. try { cfg = JSON.parse(s); } catch(ex) {}
  39. if(typeof cfg != 'object') cfg = {}; else if(cfg.version == def.version) return cfg;
  40. for(var dp in def) {
  41. if(!def.hasOwnProperty(dp) || typeof cfg[dp] == typeof def[dp]) continue;
  42. var val = GM_getValue(dp);
  43. cfg[dp] = typeof val == 'undefined' ? def[dp] : val;
  44. GM_deleteValue(dp);
  45. }
  46. for(var cp in cfg) {
  47. if(!def.hasOwnProperty(cp)) delete cfg[cp];
  48. }
  49. cfg.version = def.version;
  50. if(save) saveCfg(cfg);
  51. return cfg;
  52. }
  53.  
  54. function saveCfg(newCfg) {
  55. GM_setValue('cfg', JSON.stringify(cfg = newCfg));
  56. }
  57.  
  58. function loadHosts() {
  59. var hosts = [
  60. /* DO NOT EDIT THE CODE. USE GREASEMONKEY ICON -> USER SCRIPT COMMANDS -> SET UP... */
  61. {r:/500px\.com\/photo\//, q:'.the_photo'},
  62. {r:/attachment\.php.+attachmeantid/},
  63. {r:/abload\.de\/image/, q:'#image'},
  64. {r:/(ecx\.images-amazon\.com\/images\/I\/.+?)\./, s:function(m, node) { return node.parentNode.classList.contains('main-image-inner-wrapper') || node.parentNode.querySelector('#twister-main-image') ? '' : 'http://' + m[1] + '.jpg'; }},
  65. {r:/depic\.me\/[0-9a-z]{8,}/, q:'#pic'},
  66. {r:/deviantart\.com\/art\//, s:function(m, node) { return /\b(film|lit)/.test(node.className) || /in Flash/.test(node.title) ? '' : m.input; }, q:['#download-button[href*=".jpg"], #download-button[href*=".gif"], #download-button[href*=".png"], #gmi-ResViewSizer_fullimg', 'img.dev-content-full']},
  67. {r:/disqus\.com/, s:''},
  68. {r:/dropbox\.com\/s\/.+\.(jpe?g|gif|png)/i, q:'#download_button_link'},
  69. {r:/ebay\.[^\/]+\/itm\//, q:function(text) { return text.match(/https?:\/\/i\.ebayimg\.com\/[^\.]+\.JPG/i)[0].replace(/~~60_\d+/, '~~60_57'); }},
  70. {r:/i.ebayimg.com/, s:function(m ,node) { if(node.parentNode.querySelector('.zoom_trigger_mask')) return ''; return m.input.replace(/~~60_\d+/, '~~60_57'); }},
  71. {r:/fastpic\.ru\/view\//, q:'#image'},
  72. {r:/(fbcdn|fbexternal).*?(app_full_proxy|safe_image).+?(src|url)=(http.+?)[&\"']/, s:function(m, node) { return node.parentNode.className.indexOf('video') > -1 && m[4].indexOf('fbcdn') > -1 ? '' : decodeURIComponent(m[4]); }, html:true},
  73. {r:/facebook\.com\/photo/, s:function(m, node) { if(node.id == 'fbPhotoImage') return false; return m.input; }, q:['a.fbPhotosPhotoActionsItem[href$="dl=1"]', '#fbPhotoImage', '#root img', '#root i.img'], rect:'#fbProfileCover'},
  74. {r:/fbcdn.+?[0-9]+_([0-9]+)_[0-9]+_[a-z]\.jpg/, s:function(m, node) { try { if(/[\.^]facebook\.com$/.test(location.hostname)) return unsafeWindow.PhotoSnowlift.getInstance().stream.cache.image[m[1]].url; } catch(ex) {} return false; }, manual:true},
  75. {r:/(https?:\/\/(fbcdn-[\w\.\-]+akamaihd|[\w\.\-]+?fbcdn)\.net\/[\w\/\.\-]+?)_[a-z]\.jpg/, s:function(m, node) { if(node.id == 'fbPhotoImage') { var a = d.body.querySelector('a.fbPhotosPhotoActionsItem[href$="dl=1"]'); if(a) { return a.href.indexOf(m.input.match(/[0-9]+_[0-9]+_[0-9]+/)[0]) > -1 ? '' : a.href; } } if(node.parentNode.outerHTML.indexOf('/hovercard/') > -1) return ''; var gp = node.parentNode.parentNode; if(node.outerHTML.indexOf('profile') > 1 && gp.href && gp.href.indexOf('/photo') > -1) return false; return m[1].replace(/\/[spc][\d\.x]+/g, '') + '_n.jpg'; }, rect:'.photoWrap'},
  76. {r:/firepic\.org\/\?v=/, q:'.well img[src*="firepic.org"]'},
  77. {r:/flickr\.com\/photos\/([0-9]+@N[0-9]+|[a-z0-9_\-]+)\/([0-9]+)/, s:'http://www.flickr.com/photos/$1/$2/sizes/l/', q:'#allsizes-photo > img'},
  78. {r:/gifbin\.com\/.+\.gif/, xhr:true},
  79. {r:/(gfycat\.com\/[a-z]+)/i, s:'http://$1', q:'#webmsource'},
  80. {r:/googleusercontent\.com\/gadgets\/proxy.+?(http.+?)&/, s:function(m, node) { return decodeURIComponent(m[1]); }},
  81. {r:/googleusercontent\.com\//, s:function(m, node) { if(location.hostname == 'apis.google.com' || node.outerHTML.match(/favicons\?|\b(Ol Rf Ep|Ol Zb ag|Zb HPb|Zb Gtb|Rf Pg)\b/)) return ''; return m.input.replace(/\/(s\d{2,}[ck\-]*?|w\d+-h\d+(-[po])?)\//g, '/s0/'); }},
  82. {r:/heberger-image\.fr\/images/, q:'#myimg'},
  83. {r:/hostingkartinok\.com\/show-image\.php.*/, q:'.image img'},
  84. {r:/imagearn\.com\/image/, q:'#img', xhr:true},
  85. {r:/imagefap\.com\/(image|photo)/, q:'#gallery + noscript'},
  86. {r:/imagebam\.com\/image\//, q:'img[id]'},
  87. {r:/imageban\.(ru|net)\/show|imgnova\.com|cweb-pix\.com|(imagebunk|imagewaste)\.com\/(image|pictures\/[0-9]+)/, q:'#img_obj', xhr:true},
  88. {r:/(imagepdb\.com|imgsure\.com)\/\?v=([0-9]+)/, s:'http://$1/images/$2.jpg', xhr:true},
  89. {r:/imageshack\.us\/((i|f|photo)\/|my\.php)/, q:['div.codes > div + div', '#main_image, #fullimg']},
  90. {r:/imageshost\.ru\/photo\//i, q:'#bphoto'},
  91. {r:/imageteam\.org\/img/, q:'img[alt="image"]'},
  92. {r:/(imagetwist\.com|imageshimage\.com|imgflare\.com|imgearn\.net)\/[a-z0-9]{8,}/, q:'img.pic', xhr:true},
  93. {r:/imageupper\.com\/i\//, q:'#img', xhr:true},
  94. {r:/imagepix\.org\/image\/(.+)\.html$/, s:'http://imagepix.org/full/$1.jpg', xhr:true},
  95. {r:/imageporter\.com\/i\//, s:'/_t//', xhr:true},
  96. {r:/imagevenue\.com\/img\.php/, q:'#thepic'},
  97. {r:/imagezilla\.net\/show\//, q:'#photo', xhr:true},
  98. {r:/media-imdb\.com\/images\/.+?\.jpg/, s:'/V1\\.?_.+?\\.//g'},
  99. {r:/imgbox\.com\/([a-z0-9]+)$/i, q:'#img', xhr:location.hostname != 'imgbox.com'},
  100. {r:/imgchili\.(net|com)\/show/, q:'#show_image', xhr:true},
  101. {r:/img(money|goo)\.com\/img-/, q:'img.centred_resized', xhr:true, post:'imgContinue=Continue%20to%20image%20...%20'},
  102. {r:/imgpaying\.com\/([a-z0-9]+)\/.+html$/, q:'img.pic', xhr:true, post:function(m) { return 'op=view&id=' + m[1] + '&pre=1&submit=Continue%20to%20image...'; }},
  103. {r:/imgrill\.com\/upload\//, s:'/small/big/', xhr:true},
  104. {r:/imgtheif\.com\/image\//, q:'a > img[src*="/pictures/"]'},
  105. {r:/imgur\.com\/(a|gallery)\/([a-z0-9]+)/i, s:function(m, node) { return 'http://' + m[0] + (m[1] == 'a' ? '/noscript' : ''); }, g:{entry:'div.album-image, #image-container > div.image, #image > div.image', image:'img', caption:['h2', 'div.description'], title:'meta[name="twitter:title"]', fix:function(s) { return s.replace(/([^\/]{7})h\.(gif|jpg|png)$/, '$1.$2').replace(/^imgur.*| - Imgur$/, '');}}},
  106. {r:/imgur\.com\/(r\/[a-z]+\/|[a-z0-9]+#)?([a-z0-9]{5,})b?($|\?)/i, s:'http://i.imgur.com/$2.jpg'},
  107. {r:/instagr(\.am|am\.com)\/p\//i, q:['meta[property="og:video"]', 'meta[property="og:image"]']},
  108. {r:/(istoreimg\.com\/i|itmages\.ru\/image\/view)\//, q:'#image'},
  109. {r:/(lazygirls\.info\/.+_.+?\/[a-z0-9_]+)($|\?)/i, s:'http://www.$1?display=fullsize', q:'img.photo', xhr:location.hostname != 'www.lazygirls.info'},
  110. {r:/ld-host\.de\/show/, q:'#image'},
  111. {r:/(listal|lisimg)\.com\/(view)?image\/([0-9]+)/, s:'http://ilarge.listal.com/image/$3/0full.jpg'},
  112. {r:/(livememe\.com|lvme\.me)\/([^\.]+)$/, s:'http://i.lvme.me/$2.jpg'},
  113. {r:/lostpic\.net\/\?(photo|view)/, q:'.casem img'},
  114. {r:/modelmayhem\.com\/photos\//, s:'/_m//'},
  115. {r:/modelmayhem\.com\/avatars\//, s:'/_t/_m/'},
  116. {r:/(min\.us|minus\.com)\/(i\/|l)([a-z0-9]+)$/i, s:'http://i.min.us/i$3.jpg'},
  117. {r:/(min\.us|minus\.com)\/m[a-z0-9]+$/i, g:function(text) { var m = /gallerydata = ({[\w\W]+?});/.exec(text), o = JSON.parse(m[1]), items = []; items.title = o.name; for(var i = 0, len = o.items.length, cur; i < len && (cur = o.items[i]); i++) { items.push({url:'http://i.min\.us/i' + cur.id + '.jpg', desc:cur.caption}); }; return items; }},
  118. {r:/(panoramio\.com\/.*?photo(\/|_id=)|google\.com\/mw-panoramio\/photos\/[a-z]+\/)(\d+)/, s:'http://static.panoramio.com/photos/original/$3.jpg'},
  119. {r:/(\d+\.photobucket\.com\/.+\/)(\?[a-z=&]+=)?(.+\.(jpe?g|png|gif))/, s:'http://i$1$3', xhr:location.hostname.indexOf('photobucket.com') < 0},
  120. {r:/(photosex\.biz|posteram\.ru)\/.+?id=/i, q:'img[src*="/pic_b/"]', xhr:true},
  121. {r:/pic4all\.eu\/(images\/|view\.php\?filename=)(.+)/, s:'http://$1/images/$3'},
  122. {r:/piccy\.info\/view3\/(.*)\//, s:'http://piccy.info/view3/$1/orig/', q:'#mainim'},
  123. {r:/picsee\.net\/([\d\-]+)\/(.+?)\.html/,s:'http://picsee.net/upload/$1/$2'},
  124. {r:/picturescream\.com\/\?v=/, q:'#imagen img'},
  125. {r:/(picturescream\.[a-z\/]+|imagescream\.com\/img)\/(soft|x)/, q:'a > img[src*="/images/"]'},
  126. {r:/pimpandhost\.com\/(image|guest)\//, q:'#image'},
  127. {r:/pixhost\.org\/show\//, q:'#show_image', xhr:true},
  128. {r:/pixhub\.eu\/images/, q:'.image-show img', xhr:true},
  129. {r:/(pixroute|imgspice)\.com\/.+\.html$/, q:'img[id]', xhr:true},
  130. {r:/(pixsor\.com|euro-pic\.eu)\/share-([a-z0-9_]+)/i, s:'http://www.$1/image.php?id=$2', xhr:true},
  131. {r:/postima?ge?\.org\/image\//, q:'center img'},
  132. {r:/radikal\.ru\/(fp|.+\.html)/, q:function(text) { return text.match(/http:\/\/[a-z0-9]+\.radikal\.ru[a-z0-9\/]+\.(jpg|gif|png)/i)[0] }},
  133. {r:/screenlist\.ru\/details/, q:'#picture'},
  134. {r:/sharenxs\.com\/.+original$/, q:'img.view_photo', xhr:true},
  135. {r:/sharenxs\.com\/(gallery|view)\//, q:'a[href$="original"]', follow:true},
  136. {r:/sndcdn\.com.+/, s:function(m, node) { return node.width == 40 && navigator.userAgent.indexOf('WebKit') > -1 || /commentBubble|commentItem|carouselItem/.test(node.className + node.parentNode.className) ? '' : m.input.replace(/large|t[0-9]+x[0-9]+/, 't500x500'); }},
  137. {r:/stooorage\.com\/show\//, q:'#page_body div div img', xhr:true},
  138. {r:/(swoopic\.com|(imgproof|imgserve)\.net)\/img-/, q:'img.centred_resized, img.centred', xhr:true},
  139. {r:/turboimagehost\.com\/p\//, q:'#imageid', xhr:true},
  140. {r:/(([a-z0-9]+\.twimg\.com|twimg.*?\.akamaihd\.net)\/profile_images\/.+)_[a-z]+(\..+)/i, s:'http://$1$3'},
  141. {r:/([a-z0-9]+\.twimg\.com\/media\/[a-z0-9_-]+\.(jpe?g|png|gif))/i, s:'https://$1:large', html:true},
  142. {r:/tumblr\.com.+_500\.jpg/, s:'/_500/_1280/'},
  143. {r:/twimg\.com\/1\/proxy.+?t=(.+?)[&_]/i, s:function(m) { return wn.atob(m[1]).match(/http.+/); }},
  144. {r:/pic\.twitter\.com\/[a-z0-9]+/i, q:function(text) { return text.match(/https?:\/\/twitter\.com\/[^\/]+\/status\/\d+\/photo\/\d+/i)[0]; }, follow:true},
  145. {r:/twitpic\.com(\/show\/[a-z]+)?\/([a-z0-9]+)($|#)/i, s:'http://twitpic.com/show/large/$2'},
  146. {r:/twitter\.com\/.+\/status\/.+\/photo\//, q:'.media img', follow:true},
  147. {r:/upix\.me\/files/, s:'/#//'},
  148. {r:/(vine|seenive)\.com?\/v\//, q:'video source, meta[property="twitter:player:stream"]'},
  149. {r:/wiki.+\/thumb\/.+\.(jpe?g|gif|png|svg)\//i, s:'/\\/thumb(?=\\/)|\\/[^\\/]+$//g'},
  150. {r:/(userserve-ak\.last\.fm\/serve\/).+?(\/\d+)/, s:'http://$1_$2/0.jpg', html:true},
  151. {r:/((xxxhost|tinypix)\.me|(xxxces|imgtiger)\.com)\/viewer/, q:['.text_align_center > img', 'img[alt]'], xhr:true},
  152. {r:/(i[0-9]+\.ytimg\.com\/vi\/[^\/]+)/, s:'https://$1/0.jpg', rect:'.video-list-item'},
  153. {r:/\/\/([^\/]+)\/viewer\.php\?file=(.+)/, s:'http://$1/images/$2', xhr:true},
  154. {r:/\/albums.+\/thumb_[^\/]/, s:'/thumb_//'},
  155. {r:/\/\/[^\?]+\.(jpe?g|gif|png|svg)($|\?)/i}
  156. ];
  157. if(cfg.hosts) {
  158. var lines = cfg.hosts.split(/[\r\n]+/);
  159. for(var i = lines.length, s; i-- && (s = lines[i]);) {
  160. try {
  161. var h = JSON.parse(s);
  162. if(!h.r) throw 'property r missing';
  163. h.r = new RegExp(h.r, 'i');
  164. if(h.s && h.s.indexOf('return ') > -1) h.s = new Function('m', 'node', h.s);
  165. if(h.q && h.q.indexOf('return ') > -1) h.q = new Function('text', h.q);
  166. hosts.splice(0, 0, h);
  167. } catch(ex) {
  168. showError('Invalid host: ' + s + '\nReason: ' + ex);
  169. }
  170. }
  171. }
  172. return hosts;
  173. }
  174.  
  175. function onMouseOver(e) {
  176. if(!enabled || e.shiftKey || _.zoom || !activate(e.target)) return;
  177. _.cx = e.clientX;
  178. _.cy = e.clientY;
  179. if(cfg.start == 'auto' && !_.manual)
  180. _.timeout = wn.setTimeout(startPopup, cfg.delay);
  181. else if(cfg.start != 'auto' && e.ctrlKey)
  182. startPopup();
  183. else
  184. setStatus('ready');
  185. }
  186.  
  187. function onMouseOut(e) {
  188. if(!e.relatedTarget && !e.shiftKey) deactivate();
  189. }
  190.  
  191. function onMouseMove(e) {
  192. _.cx = e.clientX;
  193. _.cy = e.clientY;
  194. var r = _.rect;
  195. _.cr = _.cx < r.right + 2 && _.cx > r.left - 2 && _.cy < r.bottom + 2 && _.cy > r.top - 2;
  196. if(e.shiftKey) return;
  197. if(!_.zoomed && !_.cr) return deactivate();
  198. if(_.zoom) {
  199. placePopup();
  200. if(!cfg.cursor) {
  201. var bx = _.view.width/6, by = _.view.height/6;
  202. setStatus(_.cx < bx || _.cx > _.view.width - bx || _.cy < by || _.cy > _.view.height - by ? 'edge' : false);
  203. }
  204. }
  205. }
  206.  
  207. function onMouseDown(e) {
  208. if(e.which != 3 && !e.shiftKey) deactivate(true);
  209. }
  210.  
  211. function onMouseScroll(e) {
  212. var dir = (e.deltaY || -e.wheelDelta) > 0 ? 1 : -1;
  213. if(_.zoom) {
  214. drop(e);
  215. _.scale *= dir > 0 ? 0.5 : 2;
  216. if(_.scale < _.minScale) {
  217. if(!_.gItems || _.gItems.length < 2) return deactivate(true);
  218. _.scale = scale(true);
  219. _.zoom = false;
  220. }
  221. placePopup();
  222. setTitle();
  223. } else if(_.gItems && _.gItems.length > 1 && _.popup) {
  224. drop(e);
  225. nextGalleryItem(dir);
  226. } else if(cfg.zoom == 'wheel' && dir < 0 && _.popup) {
  227. drop(e);
  228. toggleZoom();
  229. } else {
  230. deactivate();
  231. }
  232. }
  233.  
  234. function onKeyDown(e) {
  235. if(e.keyCode == 17 && (cfg.start != 'auto' || _.manual) && !_.popup) startPopup();
  236. }
  237.  
  238. function onKeyUp(e) {
  239. switch(e.keyCode) {
  240. case 16:
  241. _.popup && (_.zoomed || !('cr' in _) || _.cr) ? toggleZoom() : deactivate(true);
  242. break;
  243. case 17:
  244. if(cfg.start == 'auto' && !_.manual) deactivate(true);
  245. break;
  246. case 27:
  247. if(cfg.killswitch) enabled = false;
  248. deactivate(!cfg.killswitch);
  249. break;
  250. case 39:
  251. case 74:
  252. drop(e);
  253. nextGalleryItem(1);
  254. break;
  255. case 37:
  256. case 75:
  257. drop(e);
  258. nextGalleryItem(-1);
  259. break;
  260. case 84:
  261. GM_openInTab(_.popup.src);
  262. deactivate();
  263. break;
  264. default:
  265. deactivate(true);
  266. }
  267.  
  268. }
  269.  
  270. function onContext(e) {
  271. if(e.shiftKey) return;
  272. if(cfg.zoom == 'context' && _.popup && toggleZoom()) return drop(e);
  273. if((cfg.start == 'context' || (cfg.start == 'auto' && _.manual)) && !_.status && !_.popup) {
  274. startPopup();
  275. return drop(e);
  276. }
  277. wn.setTimeout(function() { deactivate(true); }, 10);
  278. }
  279.  
  280. function startPopup() {
  281. setStatus(false);
  282. _.g ? startGalleryPopup() : startSinglePopup(_.url);
  283. }
  284.  
  285. function startSinglePopup(url) {
  286. setStatus('loading');
  287. if(!_.q) return _.xhr ? downloadImage(url, _.url) : setPopup(url);
  288. parsePage(url, _.q, _.post, function(iurl) {
  289. if(!iurl) throw 'File not found.';
  290. if(_.follow) {
  291. var info = findInfo([iurl], _.node, true);
  292. if(!info || !info.url) throw "Couldn't follow URL: " + iurl;
  293. for(var prop in info) _[prop] = info[prop];
  294. return startSinglePopup(_.url);
  295. }
  296. if(existsUnscaled(iurl, _.view)) return setStatus(false);
  297. if(_.xhr) downloadImage(iurl, url); else setPopup(iurl);
  298. });
  299. }
  300.  
  301. function startGalleryPopup() {
  302. setStatus('loading');
  303. downloadPage(_.url, null, function(text, url) {
  304. try {
  305. _.gIndex = -1;
  306. _.gItems = _.g(text, url);
  307. if(_.gItems.length == 0) {
  308. _.gItems = false;
  309. throw 'empty';
  310. }
  311. } catch(ex) {
  312. showError('Parsing error: ' + ex);
  313. }
  314. if(_.gItems) nextGalleryItem(1);
  315. });
  316. }
  317.  
  318. function loadGalleryParser(g) {
  319. if(typeof g == 'function') return g;
  320. if(typeof g == 'string') return new Function('text', g);
  321. return function(text, url) {
  322. var qE = g.entry, qC = g.caption, qI = g.image, qT = g.title, fix = (typeof g.fix == 'string' ? new Function('s', g.fix) : g.fix) || function(s) { return s.trim(); };
  323. var doc = d.implementation.createHTMLDocument('MPIV');
  324. doc.documentElement.innerHTML = text;
  325. var nodes = doc.querySelectorAll(qE), items = [];
  326. if(!Array.isArray(qC)) qC = [qC];
  327. for(var i = 0, node, len = nodes.length; i < len && (node = nodes[i]); i++) {
  328. var item = {};
  329. try {
  330. item.url = fix(findFile(node.querySelector(qI), url));
  331. item.desc = qC.reduce(function(prev, q) {
  332. var n = node.querySelector(q);
  333. if(!n) {
  334. var ps = node.previousElementSibling;
  335. if(ps && matches(ps, qE) === false) n = matches(ps, q) ? ps : ps.querySelector(q);
  336. }
  337. return n ? (prev ? prev + ' - ' : '') + fix(n.textContent) : prev;
  338. }, '');
  339. } catch(ex) {}
  340. if(item.url) items.push(item);
  341. }
  342. var title = doc.querySelector(qT);
  343. if(title) items.title = fix(title.getAttribute('content') || title.textContent);
  344. return items;
  345. };
  346. }
  347.  
  348. function nextGalleryItem(dir) {
  349. if(dir > 0 && ++_.gIndex >= _.gItems.length)
  350. _.gIndex = 0;
  351. else if(dir < 0 && --_.gIndex < 0)
  352. _.gIndex = _.gItems.length - 1;
  353. var item = _.gItems[_.gIndex];
  354. setPopup(false);
  355. startSinglePopup(item.url);
  356. var c = _.gItems.length > 1 ? '[' + (_.gIndex + 1) + '/' + _.gItems.length + '] ' : '';
  357. if(_.gIndex == 0 && _.gItems.title && (!item.desc || item.desc.indexOf(_.gItems.title) < 0)) c += _.gItems.title + (item.desc ? ' - ' : '');
  358. if(item.desc) c += item.desc;
  359. if(c) setBar(c.trim(), 'gallery', true);
  360. var preIdx = _.gIndex + dir;
  361. if(preIdx >= 0 && preIdx < _.gItems.length) {
  362. var preUrl = _.gItems[preIdx].url;
  363. on(_.popup, 'load', function() {
  364. var img = d.createElement('img');
  365. img.src = preUrl;
  366. });
  367. }
  368. }
  369.  
  370. function activate(node) {
  371. if(node == _.popup || node == d.body || node == d.documentElement) return;
  372. var info = parseNode(node);
  373. if(!info || !info.url || info.node == _.node || existsUnscaled(info.url, info.view)) return;
  374. deactivate();
  375. _ = info;
  376. [_.node, _.node.parentNode, _.node.firstElementChild].forEach(function(n) {
  377. if(n && n.title) {
  378. _.tooltip = n.title;
  379. n.title = '';
  380. }
  381. });
  382. _.style = d.createElement('style');
  383. _.style.textContent = '\
  384. #mpiv-bar { position:fixed;z-index:2147483647;left:0;right:0;top:0;transform:scaleY(0);-webkit-transform:scaleY(0);transform-origin:top;-webkit-transform-origin:top;transition:transform 500ms ease 1000ms;-webkit-transition:-webkit-transform 500ms ease 1000ms;text-align:center;font-family:sans-serif;font-size:15px;font-weight:bold;background:rgba(0, 0, 0, 0.6);color:white;padding:4px 10px; }\
  385. #mpiv-bar.mpiv-show { transform:scaleY(1);-webkit-transform:scaleY(1); }\
  386. #mpiv-popup { display:none;border:1px solid gray;background-color:white;position:fixed;z-index:2147483647;margin:0;max-width:none;max-height:none;cursor:' + (cfg.cursor ? 'default' : 'none') + '; }\
  387. #mpiv-popup.mpiv-show { display:inline; }\
  388. #YTLT-preview, body > div.tipsy { display:none!important; }\
  389. .mpiv-loading * { cursor:wait!important; }\
  390. .mpiv-edge #mpiv-popup { cursor:default; }\
  391. .mpiv-error * { cursor:not-allowed!important; }\
  392. .mpiv-ready *, .mpiv-large * { cursor:zoom-in!important; cursor:-webkit-zoom-in!important; }' +
  393. (cfg.css.indexOf('{') < 0 ? '#mpiv-popup {' + cfg.css + '}' : cfg.css);
  394. d.head.appendChild(_.style);
  395. on(d, 'mousemove', onMouseMove);
  396. on(d, 'mouseout', onMouseOut);
  397. on(d, 'mousedown', onMouseDown);
  398. on(d, 'contextmenu', onContext);
  399. on(d, 'keydown', onKeyDown);
  400. on(d, 'keyup', onKeyUp);
  401. on(d, 'onwheel' in d ? 'wheel' : 'mousewheel', onMouseScroll);
  402. return true;
  403. }
  404.  
  405. function deactivate(wait) {
  406. wn.clearTimeout(_.timeout);
  407. if(_.req && 'abort' in _.req) _.req.abort();
  408. if(_.node && _.tooltip) _.node.title = _.tooltip;
  409. setTitle(true);
  410. setStatus(false);
  411. setPopup(false);
  412. setBar(false);
  413. rm(_.style);
  414. _ = {};
  415. off(d, 'mousemove', onMouseMove);
  416. off(d, 'mouseout', onMouseOut);
  417. off(d, 'mousedown', onMouseDown);
  418. off(d, 'contextmenu', onContext);
  419. off(d, 'keydown', onKeyDown);
  420. off(d, 'keyup', onKeyUp);
  421. off(d, 'onwheel' in d ? 'wheel' : 'mousewheel', onMouseScroll);
  422. if(wait) {
  423. enabled = false;
  424. wn.setTimeout(function() { enabled = true; }, 200);
  425. }
  426. }
  427.  
  428. function parseNode(node) {
  429. var info;
  430. if(tag(node) == 'IMG' && node.src.substr(0, 5) != 'data:') {
  431. var src = rel2abs(node.src, location.href);
  432. info = findInfo([src], node);
  433. if(info) return info;
  434. }
  435. if(tag(node.parentNode) == 'A') node = node.parentNode; else if(tag(node.parentNode.parentNode) == 'A') node = node.parentNode.parentNode;
  436. if(tag(node) == 'A') {
  437. if(cfg.thumbsonly && !node.querySelector('img, i') && !hasBg(node) && !hasBg(node.parentNode) && !hasBg(node.firstElementChild)) return;
  438. var url = decodeURIComponent(node.getAttribute('data-expanded-url') || node.href);
  439. var urls = url.indexOf('//t.co/') > -1 ? ['http://' + node.textContent] : parseUrls(url);
  440. info = findInfo(urls, node);
  441. if(info) return info;
  442. }
  443. if(tag(node) == 'IMG' || tag(node) == 'A' && (node = node.querySelector('img'))) {
  444. return {url:node.src, node:node, rect:rect(node), view:viewRect()};
  445. }
  446. }
  447.  
  448. function findInfo(urls, node, noHtml) {
  449. var html = noHtml ? false : node.outerHTML;
  450. if(!hosts) hosts = loadHosts();
  451. for(var i = 0, len = hosts.length, h, m; i < len && (h = hosts[i]); i++) {
  452. if(!(m = h.html && html ? h.r.exec(html) : findMatch(urls, h.r))) continue;
  453. if(tag(node) == 'IMG' && !h.s) continue;
  454. var info = {
  455. node: node,
  456. url: 's' in h ? (typeof h.s == 'function' ? h.s(m, node) : replace(h.s, m)) : m.input,
  457. r: h.r,
  458. q: h.q,
  459. g: h.g ? loadGalleryParser(h.g) : h.g,
  460. xhr: h.xhr,
  461. post: typeof h.post == 'function' ? h.post(m) : h.post,
  462. follow: h.follow,
  463. manual: h.manual,
  464. rect: rect(node, h.rect),
  465. view: viewRect()
  466. };
  467. if(info.url === false) continue;
  468. return info;
  469. };
  470. }
  471.  
  472. function downloadPage(url, post, cb) {
  473. var opts = {
  474. method: 'GET',
  475. url: url,
  476. ignoreCache: true,
  477. onload: function(req) {
  478. try {
  479. delete _.req;
  480. if(req.status > 399) throw 'Server error: ' + req.status;
  481. cb(req.responseText, req.finalUrl || url);
  482. } catch(ex) {
  483. showError(ex);
  484. }
  485. },
  486. onerror: showError
  487. };
  488. if(post) {
  489. opts.method = 'POST';
  490. opts.data = post;
  491. opts.headers = {'Content-Type':'application/x-www-form-urlencoded','Referer':url};
  492. }
  493. _.req = GM_xmlhttpRequest(opts);
  494. }
  495.  
  496. function downloadImage(url, referer) {
  497. var start = Date.now(), cap;
  498. _.req = GM_xmlhttpRequest({
  499. method: 'GET',
  500. url: url,
  501. overrideMimeType: 'text/plain; charset=x-user-defined',
  502. headers: {'Accept':'image/png,image/*;q=0.8,*/*;q=0.5','Referer':referer},
  503. onprogress: function(e) {
  504. if(!cap && (Date.now() - start > 3000) && (e.loaded/e.total < 0.5)) cap = true;
  505. if(cap) setBar(parseInt(e.loaded/e.total * 100) + '%', 'xhr');
  506. },
  507. onload: function(req) {
  508. try {
  509. delete _.req;
  510. setBar(false);
  511. if(req.status > 399) throw 'HTTP error ' + req.status;
  512. var txt = req.responseText, ui8 = new Uint8Array(txt.length);
  513. for(var i = txt.length; i--;) {
  514. ui8[i] = txt.charCodeAt(i);
  515. }
  516. var b = new Blob([ui8.buffer]);
  517. var u = wn.URL || wn.webkitURL;
  518. if(u) return setPopup(u.createObjectURL(b));
  519. var fr = new FileReader();
  520. fr.onload = function() { setPopup(fr.result); };
  521. fr.onerror = showError;
  522. fr.readAsDataURL(b);
  523. } catch(ex) {
  524. showError(ex);
  525. }
  526. },
  527. onerror: showError
  528. });
  529. }
  530.  
  531. function parsePage(url, q, post, cb) {
  532. downloadPage(url, post, function(html) {
  533. if(typeof q == 'function') return cb(q(html));
  534. var node, doc = d.implementation.createHTMLDocument('MPIV');
  535. doc.documentElement.innerHTML = html;
  536. if(Array.isArray(q)) {
  537. for(var i = 0, len = q.length; i < len; i++) {
  538. node = doc.querySelector(q[i]);
  539. if(node) break;
  540. }
  541. } else {
  542. node = doc.querySelector(q);
  543. }
  544. cb(node ? findFile(node, url) : false);
  545. });
  546. }
  547.  
  548. function findFile(n, url) {
  549. var base = n.ownerDocument.querySelector('base[href]');
  550. var path = n.getAttribute('src') || n.getAttribute('href') || /https?:\/\/[.\/a-z0-9_+%\-]+\.(jpe?g|gif|png|svg|webm|mp4)/i.exec(n.outerHTML) && RegExp.lastMatch;
  551. return path ? rel2abs(path.trim(), base ? base.getAttribute('href') : url) : false;
  552. }
  553.  
  554. function checkProgress(start) {
  555. if(start === true) {
  556. if(checkProgress.interval) wn.clearInterval(checkProgress.interval);
  557. checkProgress.interval = wn.setInterval(checkProgress, 150);
  558. return;
  559. }
  560. var p = _.popup;
  561. if(!p) return wn.clearInterval(checkProgress.interval);
  562. _.nheight = p.naturalHeight || p.videoHeight;
  563. _.nwidth = p.naturalWidth || p.videoWidth;
  564. if(!_.nheight) return;
  565. wn.clearInterval(checkProgress.interval);
  566. setStatus(false);
  567. p.clientHeight;
  568. p.className = 'mpiv-show';
  569. var s = wn.getComputedStyle(p);
  570. _.pw = styleSum(s, ['padding-left', 'padding-right']);
  571. _.ph = styleSum(s, ['padding-top', 'padding-bottom']);
  572. _.mbw = styleSum(s, ['margin-left', 'margin-right', 'border-left-width', 'border-right-width']);
  573. _.mbh = styleSum(s, ['margin-top', 'margin-bottom', 'border-top-width', 'border-bottom-width']);
  574. _.scale = scale(true);
  575. placePopup();
  576. setTitle();
  577. if(_.tooltip && !_.bar) setBar(_.tooltip, 'tooltip');
  578. if(_.large = _.nwidth > p.clientWidth + _.mbw || _.nheight > p.clientHeight + _.mbh) setStatus('large');
  579. if(cfg.zoom == 'auto') toggleZoom();
  580. }
  581.  
  582. function placePopup() {
  583. var p = _.popup;
  584. if(!p) return;
  585. var x = null, y = null, w = Math.round(_.scale * _.nwidth), h = Math.round(_.scale * _.nheight), cx = _.cx, cy = _.cy, vw = _.view.width, vh = _.view.height;
  586. if(!_.zoom && (!_.gItems || _.gItems.length < 2) && !cfg.center) {
  587. var r = _.rect, rx = (r.left + r.right) / 2, ry = (r.top + r.bottom) / 2;
  588. if(vw - r.right - 40 > w + _.mbw || w + + _.mbw < r.left - 40) {
  589. if(h + _.mbh < vh - 60) y = Math.min(Math.max(ry - h/2, 30), vh - h - 30);
  590. x = rx > vw/2 ? r.left - 40 - w : r.right + 40;
  591. } else if(vh - r.bottom - 40 > h + _.mbh || h + _.mbh < r.top - 40) {
  592. if(w + _.mbw < vw - 60) x = Math.min(Math.max(rx - w/2, 30), vw - w - 30);
  593. y = ry > vh/2 ? r.top - 40 - h : r.bottom + 40;
  594. }
  595. }
  596. if(x == null) x = Math.round((vw > w ? vw/2 - w/2 : -1 * Math.min(1, Math.max(0, 5/3*(cx/vw-0.2))) * (w - vw)) - (_.pw + _.mbw)/2);
  597. if(y == null) y = Math.round((vh > h ? vh/2 - h/2 : -1 * Math.min(1, Math.max(0, 5/3*(cy/vh-0.2))) * (h - vh)) - (_.ph + _.mbh)/2);
  598. p.style.cssText = 'width:' + w + 'px;height:' + h + 'px;left:' + x + 'px;top:' + y + 'px';
  599. }
  600.  
  601. function toggleZoom() {
  602. var p = _.popup;
  603. if(!p || !_.nheight) return;
  604. _.zoom = !_.zoom;
  605. _.zoomed = true;
  606. _.scale = _.minScale = scale(!_.zoom);
  607. placePopup();
  608. setTitle();
  609. setStatus(_.zoom ? 'zoom' : false);
  610. if(cfg.zoom != 'auto') setBar(false);
  611. return _.zoom;
  612. }
  613.  
  614. function showError(o) {
  615. var msg = o.message || (o.readyState ? 'Request failed.' : (o.type == 'error' ? 'File can\'t be displayed.' + (d.querySelector('div[bgactive*="flashblock"]') ? ' Check Flashblock settings.' : '') : o));
  616. GM_log(msg + '\nRegExp: ' + _.r +'\nURL: ' + _.url);
  617. if(_.node) { setStatus('error'); setBar(msg, 'error'); }
  618. }
  619.  
  620. function setStatus(status) {
  621. var de = d.documentElement, cn = de.className.replace(/mpiv-[a-z]+/g, '').trim();
  622. if(status) cn += ' mpiv-' + status;
  623. de.className = cn;
  624. }
  625.  
  626. function setPopup(src) {
  627. var p = _.popup;
  628. if(p) {
  629. _.zoom = false;
  630. off(p, 'error', showError);
  631. p.src = '';
  632. rm(p);
  633. delete _.popup;
  634. }
  635. if(!src) return;
  636. if(/\.(webm|mp4)($|\?)/.test(src)) {
  637. p = _.popup = d.createElement('video');
  638. p.setAttribute('autoplay', true);
  639. p.setAttribute('loop', true);
  640. } else {
  641. p = _.popup = d.createElement('img');
  642. }
  643. p.id = 'mpiv-popup';
  644. on(p, 'error', showError);
  645. _.bar ? d.body.insertBefore(p, _.bar) : d.body.appendChild(p);
  646. p.src = src;
  647. checkProgress(true);
  648. }
  649.  
  650. function setBar(label, cn) {
  651. var b = _.bar;
  652. if(!label) {
  653. rm(b);
  654. delete _.bar;
  655. return
  656. }
  657. if(!b) {
  658. b = _.bar = d.createElement('div');
  659. b.id = 'mpiv-bar';
  660. }
  661. b.innerHTML = label;
  662. if(!b.parentNode) {
  663. d.body.appendChild(b);
  664. b.clientHeight;
  665. }
  666. b.className = 'mpiv-show mpiv-' + cn;
  667. }
  668.  
  669. function setTitle(reset) {
  670. if(reset) {
  671. if(typeof _.title == 'string') d.title = _.title;
  672. } else {
  673. if(typeof _.title != 'string') _.title = d.title;
  674. var p = _.popup;
  675. d.title = _.nwidth + 'x' + _.nheight + ' @ ' + Math.round((p.clientHeight - _.ph) / _.nheight * 100) + '%';
  676. }
  677. }
  678.  
  679. function parseUrls(url) {
  680. if(url.substr(0, 4) != 'http' || url.lastIndexOf('http') < 1) return [url];
  681. var urls = url.match(/https?:.+?(?=https?:|$)/g) || [];
  682. if(urls.length < 2) return urls;
  683. return [url].concat(urls.slice(1).map(function(url) { var pos = url.indexOf('&'); return decodeURIComponent(pos < 0 ? url : url.substr(0, pos)); })).reverse();
  684. }
  685.  
  686. function findMatch(a, re) {
  687. for(var i = a.length; i--;) {
  688. var m = re.exec(a[i]);
  689. if(m) return m;
  690. }
  691. }
  692.  
  693. function rel2abs(rel, abs) {
  694. if(rel.indexOf('//') === 0) rel = 'http:' + rel;
  695. var re = /^([a-z]+:)?\/\//;
  696. if(re.test(rel)) return rel;
  697. if(!re.exec(abs)) return;
  698. if(rel[0] == '/') return abs.substr(0, abs.indexOf('/', RegExp.lastMatch.length)) + rel;
  699. return abs.substr(0, abs.lastIndexOf('/')) + '/' + rel;
  700. }
  701.  
  702. function replace(s, m) {
  703. if(s.indexOf('/') === 0) {
  704. var mid = /[^\\]\//.exec(s).index+1;
  705. var end = s.lastIndexOf('/');
  706. var re = new RegExp(s.substring(1, mid), s.substr(end+1));
  707. return m.input.replace(re, s.substring(mid+1, end));
  708. }
  709. for(var i = m.length; i--;) {
  710. s = s.replace('$'+i, m[i]);
  711. }
  712. return s;
  713. }
  714.  
  715. function styleSum(s, p) {
  716. for(var i = p.length, x = 0; i--;) {
  717. x += parseInt(s.getPropertyValue([p[i]]), 10) || 0;
  718. }
  719. return x;
  720. }
  721.  
  722. function scale(fit) {
  723. var vw = _.view.width, vh = _.view.height;
  724. return _.large && !fit ?
  725. (cfg.halfzoom && (_.nheight / vh > 3 && _.nwidth > vw || _.nwidth / vh > 3 && _.nheight > vh) ? 0.5 : 1)
  726. :
  727. Math.min((vw - _.mbw)/_.nwidth, (vh - _.mbh)/_.nheight, fit ? 1 : Number.MAX_VALUE);
  728. }
  729.  
  730. function hasBg(node) {
  731. return node ? wn.getComputedStyle(node).backgroundImage != 'none' && node.className.indexOf('YTLT-') < 0 : false;
  732. }
  733.  
  734. function existsUnscaled(url, view) {
  735. for(var i = d.images.length, img; i-- && (img = d.images[i]);) {
  736. if(img.src == url && Math.max(img.naturalHeight/img.clientHeight, img.naturalWidth/img.clientWidth) < cfg.scale) return true;
  737. }
  738. }
  739.  
  740. function viewRect() {
  741. var node = d.compatMode == 'BackCompat' ? d.body : d.documentElement;
  742. return {width:node.clientWidth, height:node.clientHeight};
  743. }
  744.  
  745. function rect(node, q) {
  746. if(q) {
  747. var n = node;
  748. while(tag(n = n.parentNode) != 'BODY') {
  749. if(matches(n, q)) return n.getBoundingClientRect();
  750. }
  751. }
  752. var nodes = node.querySelectorAll('*');
  753. for(var i = nodes.length; i-- && (n = nodes[i]);) {
  754. if(n.clientHeight > node.clientHeight) node = n;
  755. }
  756. return node.getBoundingClientRect();
  757. }
  758.  
  759. function matches(n, q) {
  760. var p = Element.prototype, m = p.mozMatchesSelector || p.webkitMatchesSelector || p.matches;
  761. if(m) return m.call(n, q);
  762. }
  763.  
  764. function tag(n) {
  765. return n.tagName.toUpperCase();
  766. }
  767.  
  768. function rm(n) {
  769. if(n && n.parentNode) n.parentNode.removeChild(n);
  770. }
  771.  
  772. function on(n, e, f) {
  773. n.addEventListener(e, f);
  774. }
  775.  
  776. function off(n, e, f) {
  777. n.removeEventListener(e, f);
  778. }
  779.  
  780. function drop(e) {
  781. e.preventDefault();
  782. e.stopPropagation();
  783. }
  784.  
  785. function setup() {
  786. var $ = function(s) { return d.getElementById('mpiv-'+s); }
  787. var close = function() { rm($('setup')); };
  788. var update = function() { $('delay').parentNode.style.display = $('start-auto').selected ? '' : 'none'; };
  789. var check = function(e) {
  790. var t = e.target, ok;
  791. try {
  792. var pes = t.previousElementSibling;
  793. if(t.value) {
  794. if(!pes) { var inp = t.cloneNode(); inp.value = ''; t.parentNode.insertBefore(inp, t); }
  795. new RegExp(JSON.parse(t.value).r);
  796. } else if(pes) {
  797. pes.focus();
  798. rm(t);
  799. }
  800. ok = 1;
  801. } catch(ex) {}
  802. t.style.backgroundColor = ok ? '' : '#ffaaaa';
  803. }
  804. var exp = function(e) {
  805. drop(e);
  806. var s = JSON.stringify(cfg);
  807. if(typeof GM_setClipboard == 'function') {
  808. GM_setClipboard(s);
  809. wn.alert('Settings copied to clipboard!');
  810. } else {
  811. wn.alert(s);
  812. }
  813. };
  814. var imp = function(e) {
  815. drop(e);
  816. var s = wn.prompt('Paste settings:');
  817. if(!s) return;
  818. init(fixCfg(s));
  819. };
  820. var init = function(cfg) {
  821. close();
  822. var big = viewRect().height > 850;
  823. GM_addStyle('\
  824. #mpiv-setup { position:fixed;z-index:2147483647;top:30px;right:30px;padding:20px 30px;background:#eee;width:580px;border:1px solid black; }\
  825. #mpiv-setup * { color:black;text-align:left;line-height:15px;font-size:12px;font-family:sans-serif;box-shadow:none; }\
  826. #mpiv-setup a { color:darkblue!important;text-decoration:underline!important; }\
  827. #mpiv-setup div { text-align:center;font-weight:bold;font-size:14px; }\
  828. #mpiv-setup ul { margin:15px 0 15px 0;padding:0;list-style:none;background:#eee;border:0; }\
  829. #mpiv-setup input, #mpiv-setup select, #mpiv-css { display:inline;border:1px solid gray;padding:2px;background:white; }\
  830. #mpiv-css { resize:vertical; height:' + (big ? 105 : 45) + 'px; }\
  831. #mpiv-setup li { margin:0;padding:8px 0;vertical-align:middle;background:#eee;border:0 }\
  832. #mpiv-delay, #mpiv-scale { width:36px; }\
  833. #mpiv-cursor, #mpiv-killswitch { margin-left:18px; }\
  834. #mpiv-hosts { max-height:' + (big ? 370 : 170) + 'px;overflow-y:auto; padding:2px; margin:4px 0; }\
  835. #mpiv-hosts input, #mpiv-css { width:98%;margin:3px 0; }\
  836. #mpiv-setup button { width:150px;margin:0 10px;text-align:center; }\
  837. ');
  838. var div = d.createElement('div');
  839. div.id = 'mpiv-setup';
  840. d.body.appendChild(div);
  841. div.innerHTML = '\
  842. <div>Mouseover Popup Image Viewer</div><ul>\
  843. <li>Popup activation: <select><option id="mpiv-start-auto">automatically</option><option id="mpiv-start-context">right click or ctrl</option><option id="mpiv-start-ctrl">ctrl</option></select> <span>after <input id="mpiv-delay" type="text"/> ms</span> over <select><option>thumbnails and text links</option><option id="mpiv-thumbsonly">thumbnails only</option></select></li>\
  844. <li>Zoom activation: <select><option id="mpiv-zoom-context">right click or shift</option><option id="mpiv-zoom-wheel">wheel up or shift</option><option id="mpiv-zoom-shift">shift</option><option id="mpiv-zoom-auto">automatically</option></select> with large images set to <select><option>100%</option><option id="mpiv-halfzoom">auto</option></select></li>\
  845. <li>Only show popup over scaled-down images when natural size is <input id="mpiv-scale" type="text"/> times larger</li>\
  846. <li><input type="checkbox" id="mpiv-center"/> Always centered <input type="checkbox" id="mpiv-cursor"/> Autohide cursor <input type="checkbox" id="mpiv-killswitch"/> Disable on Esc</li>\
  847. <li>Custom CSS:<div><textarea id="mpiv-css" spellcheck="false"></textarea></li>\
  848. <li>Custom host rules:<div id="mpiv-hosts"><input type="text" spellcheck="false"></div></li>\
  849. <li>Learn about <a href="http://w9p.co/userscripts/mpiv/css.html" target="_blank">custom CSS</a> and <a href="http://w9p.co/userscripts/mpiv/host_rules.html" target="_blank">host rules</a>.<span style="float:right"><a href="#" id="mpiv-import">Import</a> | <a href="#" id="mpiv-export">Export</a></span></li>\
  850. </ul><div><button id="mpiv-ok">OK</button><button id="mpiv-cancel">Cancel</button></div>';
  851. div = null;
  852. on($('start-auto').parentNode, 'change', update);
  853. on($('cancel'), 'click', close);
  854. on($('export'), 'click', exp);
  855. on($('import'), 'click', imp);
  856. on($('hosts'), 'input', check);
  857. if(cfg.hosts) {
  858. var parent = $('hosts');
  859. var lines = cfg.hosts.split(/[\r\n]+/);
  860. for(var i = 0, s, r; i < lines.length && (s = lines[i]); i++) {
  861. var inp = parent.firstElementChild.cloneNode();
  862. inp.value = s;
  863. parent.appendChild(inp);
  864. check({target:inp});
  865. }
  866. }
  867. on($('ok'), 'click', function() {
  868. var delay = parseInt($('delay').value, 10);
  869. if(!isNaN(delay) && delay >= 0) cfg.delay = delay;
  870. var scale = parseFloat($('scale').value.replace(',', '.'));
  871. if(!isNaN(scale)) cfg.scale = Math.max(1, scale);
  872. cfg.thumbsonly = $('thumbsonly').selected;
  873. cfg.start = $('start-context').selected ? 'context' : ($('start-ctrl').selected ? 'ctrl' : 'auto');
  874. cfg.zoom = $('zoom-context').selected ? 'context' : ($('zoom-wheel').selected ? 'wheel' : ($('zoom-shift').selected ? 'shift' : 'auto'));
  875. cfg.halfzoom = $('halfzoom').selected;
  876. cfg.center = $('center').checked;
  877. cfg.cursor = !$('cursor').checked;
  878. cfg.killswitch = $('killswitch').checked;
  879. cfg.css = $('css').value.trim();
  880. var inps = $('hosts').querySelectorAll('input'), lines = [];
  881. for(var i = 0; i < inps.length; i++) {
  882. var s = inps[i].value.trim();
  883. if(s) lines.push(s);
  884. }
  885. lines.sort();
  886. cfg.hosts = lines.join('\n');
  887. saveCfg(cfg);
  888. hosts = loadHosts();
  889. close();
  890. });
  891. $('delay').value = cfg.delay;
  892. $('scale').value = cfg.scale;
  893. $('thumbsonly').selected = cfg.thumbsonly;
  894. $('halfzoom').selected = cfg.halfzoom;
  895. $('center').checked = cfg.center;
  896. $('cursor').checked = !cfg.cursor;
  897. $('killswitch').checked = cfg.killswitch;
  898. $('css').value = cfg.css;
  899. $('zoom-' + cfg.zoom).selected = true;
  900. $('start-' + cfg.start).selected = true;
  901. update();
  902. };
  903. init(loadCfg());
  904. }
  905.  
  906. if(d.body.childElementCount != 1 || tag(d.body.firstElementChild) != 'IMG') {
  907. on(d, 'mouseover', onMouseOver);
  908. var googImgs = d.getElementById('rg_s');
  909. if(googImgs) on(googImgs, 'mouseover', onMouseOver);
  910. GM_registerMenuCommand('Set up Mouseover Popup Image Viewer', setup);
  911. }