Facebookenlarge

Enlarges pictures when you roll over them

当前为 2014-11-12 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Facebookenlarge
  3. // @namespace http://userscripts.org/users/23652
  4. // @description Enlarges pictures when you roll over them
  5. // @include http://*.facebook.com/*
  6. // @include https://*.facebook.com/*
  7. // @include http://facebook.com/*
  8. // @include https://facebook.com/*
  9. // @copyright JoeSimmons
  10. // @version 1.0.7
  11. // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
  12. // @require https://greasyfork.org/scripts/1885-joesimmons-library/code/JoeSimmons'%20Library.js?version=4838
  13. // @require https://greasyfork.org/scripts/2817-jsl-ajax-plugin/code/JSL%20-%20AJAX%20plugin.js?version=7911
  14. // @grant GM_xmlHttpRequest
  15. // ==/UserScript==
  16.  
  17. /* CHANGELOG ////////////////////////////////////////
  18.  
  19. 1.0.7 (11/12/2014)
  20. - fixed problem with some images not showing up at all
  21. it was because the script couldn't differentiate between good responses and empty responses
  22. some working images had an HTTP status of 0, and so did empty responses
  23. I've now switched to a GET request and just check if the response text is empty or not
  24. It not only preloads the image, but it can 100% check if the modified (large) image is working
  25.  
  26. 1.0.6 (7/1/2014)
  27. - fixed feature to not show enlarged thumbnail in fullscreen view or on album image hover
  28. - fixed another small error with displaying some images
  29.  
  30. 1.0.58 (6/26/2014)
  31. - fixed an error with displaying some images (removed "v/t1.0-1/" out of url)
  32. - made it not show a popup when the popup url is the same as the hovered url
  33. this prevents a popup when hovering over a photo in full screen viewing mode
  34. - added a small system to check if the enlarged url exists - if not, the user will
  35. be shown the thumbnail in the popup (no enlargement because it's not possible)
  36. instead of a small, empty, black box
  37.  
  38. 1.0.57 (4/6/2014)
  39. - fixed a problem with banners (on shared pages boxes) unable to be enlarged
  40.  
  41. 1.0.56 (3/28/2014)
  42. - fixed a problem with hovering over the "Photos" thumbnail on a page
  43. - fixed a problem with some thumbnails on the "About" page
  44.  
  45. 1.0.55 (3/22/2014)
  46. - fixed an issue where hovering over someone's cover photo wouldn't show it
  47.  
  48. 1.0.54 (3/20/2014)
  49. - allowed enlarging of the banner picture above "Like Page" button
  50. - adapted to the newest JSL
  51.  
  52. 1.0.53 (9/13/2013)
  53. - added more image compatibility
  54.  
  55. 1.0.52 (9/12/2013)
  56. - updated the 'ispic' RegExp
  57.  
  58. 1.0.51
  59. - fixed the 'ispic' RegExp. some pics weren't getting matched
  60.  
  61. 1.0.50
  62. - fixed a bug wherein the preview wouldn't always display in the correct corner
  63. - fixed a bug wherein some pictures would display incorrectly
  64. - updated HQ pic getting methods so that it shows the biggest picture possible (without ajax)
  65. - added a method so that the preview would never overlap the mouse cursor
  66. - now middle & right clicking don't hide the preview, only left-clicking
  67.  
  68. 1.0.49
  69. - fixed a regexp bug that would cause some pictures to not show
  70. - added an anonymous function wrapper
  71. - updated GM_addStyle check/function
  72.  
  73. */ //////////////////////////////////////////////////
  74.  
  75.  
  76.  
  77.  
  78. // By: Ian Williams and edited for Facebook by JoeSimmons
  79.  
  80. (function () { // anonymous function wrapper
  81.  
  82. 'use strict';
  83.  
  84. var delay = 400,
  85. coords = {
  86. 'x' : 0,
  87. 'y' : 0
  88. },
  89. size = /([\d_]{5,})([_\/])[qstna]([_\.])?/i,
  90. ispic = /https?:\/\/((fbcdn-)?(profile|s?(photos|content)-\w|s(photos|content))((-\w+)+)?(\.ak|\.xx)?\.(fbcdn|akamaihd))\.net\/(.*\/)+.*([_\/][qstna]([\._])?)?.*(jpe?g|[tg]iff?|bmp|png)([?&][a-z]+=[a-zA-Z0-9]+)*/i,
  91. rFbexternal = /&(cfs|upscale)(=[^&]+)?/g,
  92. XbyX = /(v\/t[^\/]+\/)?(\w(\d+\.)+\d+\/)?\w\d{2,4}x\d{2,4}\//,
  93. c = /\w(\d+\.)+\d+\//,
  94. app = /www\/app_full_proxy\.php/,
  95. show_d, // timeout holder
  96. docFrag = document.createDocumentFragment();
  97.  
  98. // Debug by JoeSimmons
  99. function debug() {
  100. var d = document.getElementById('debugT'),
  101. body = document.body,
  102. strings = [], i, arg;
  103.  
  104. for(i = 0; i < arguments.length; i += 1) {
  105. arg = arguments[i];
  106. if ( (typeof arg === 'string' ? arg.trim() !== '' : arg ) ) {
  107. strings.push(arg);
  108. }
  109. }
  110. if (!d && body) {
  111. d = document.createElement('pre');
  112. d.id = 'debugT';
  113. d.setAttribute('style', 'position: fixed; width: 97%; height: 20%; margin: 0 1% 6px 1%; padding: 5px; color: #000000; background: #E6F4FF; border: 3px double #0099FF; border-top: 0; z-index: 999999; scroll-y: auto; overflow: auto; white-space: pre-wrap;');
  114. d.textContent = '[Debug Window 2.0 - Drag box to read fully - Copyright Joe Simmons \'CC BY-ND 3.0\']\n\n\n\n' + strings.join('\n\n');
  115. d.addEventListener('dblclick', function (e) {e.target.style.display = 'none';}, false);
  116. body.insertBefore(d, document.body.firstChild);
  117. } else {
  118. d.textContent += '\n\n--------------------------------------------------\n\n' + strings.join('\n\n');
  119. }
  120.  
  121. if (d.style.display === 'none') d.style.display = 'block';
  122. }
  123.  
  124. function primeThumbs() {
  125. JSL('//a[@data-hovercard]/img/..').removeAttribute('data-hovercard');
  126. }
  127.  
  128. // record mouse movement for positioning the preview images
  129. function handleMove(event) {
  130. coords.x = event.pageX - pageXOffset;
  131. coords.y = event.pageY - pageYOffset;
  132. event.stopPropagation();
  133. }
  134.  
  135. function show(src) {
  136. var pop = JSL('#picPop'),
  137. x = coords.x,
  138. y = coords.y,
  139. isTop = y < (window.innerHeight / 2), // is mouse at top?
  140. isLeft = x < ( (window.innerWidth - 15 /* 15 is the scrollbar width, approx. */) / 2), // is mouse at left?
  141. maxWidth = (isLeft ? (window.innerWidth - x) : x) - 25, // keep the image at least 25px away from the cursor
  142. vert, hori;
  143.  
  144. // make sure the preview doesn't show beyond the browser dimensions or overlap the mouse cursor
  145. // this alone only limits the max-width, but coupled with the 4-corner system, it works
  146. pop.css('max-width', maxWidth + 'px');
  147.  
  148. // set the preview's source url
  149. pop.attribute('src', src);
  150.  
  151. // determine where the preview should display according to hovered image's position
  152. // ideally, as far away from the hovered image as possible
  153. vert = isTop ? 'bottom' : 'top'; // should the preview be on the top or bottom?
  154. hori = isLeft ? 'right' : 'left'; // should the preview be on the left or right?
  155.  
  156. // reset the position of the hover box
  157. pop.css('top', 'auto').css('right', 'auto').css('bottom', 'auto').css('left', 'auto');
  158.  
  159. // set the corner it will appear in
  160. pop.css(vert, '0').css(hori, '0');
  161.  
  162. // show the preview
  163. pop.show('block');
  164. }
  165.  
  166. function handleMouseover(event) {
  167. var t = event.target,
  168. tag = t.tagName.toLowerCase(),
  169. style = t.getAttribute('style'),
  170. _class = t.className,
  171. src = style && style.match(ispic) ? t.getAttribute('style') : unescape(t.src),
  172. imgExist = JSL('./img | ./i | ./div/img | ./span/img', t),
  173. odd = JSL('./../img | ./../i | ./../../div[@class="detail"]/i[@class="photo" and contains(@style, "background-image")]', t),
  174. ohoe = JSL('./ancestor::a[( contains(@href, "oh=") and contains(@href, "oe=") ) or ( contains(@href, "oh%3D") and contains(@href, "oe%3D") )]', t),
  175. snowlift = JSL('#photos_snowlift'),
  176. goThroughWithShow, hqImg;
  177.  
  178. // checks if enlarged thumbnail's url exists before showing it
  179. function checkSource(res) {
  180. if (res.responseText === '') {
  181. // if the enlarged thumbnail url has a problem, show the original thumbnail instead
  182. hidePicPop();
  183. show_d = window.setTimeout(show, 200, this);
  184. }
  185. }
  186.  
  187. // sometimes the hovered element can be a parent element of the actual thumbnail element
  188. if (imgExist.exists) {
  189. t = imgExist[0];
  190. src = unescape(t.src);
  191. tag = t.tagName.toLowerCase();
  192.  
  193. if ( src.indexOf('fbexternal') !== -1 && src.match(ispic) ) {
  194. src = src.match(ispic)[0].replace(rFbexternal, '');
  195. }
  196. }
  197.  
  198. if (ohoe.exists) {
  199. src = decodeURIComponent( ohoe.attribute('href') ).split('&src=')[1].split('&smallsrc=')[0];
  200. }
  201.  
  202. // support for elements that when hovered over, aren't the image itself
  203. // or it's an element where it displays the image by css' background-image
  204. if ( tag === 'div' && (['coverBorder', 'mat'].indexOf(_class) !== -1) ) {
  205. if (odd.exists) {
  206. t = odd[0];
  207. tag = t.tagName.toLowerCase();
  208. style = t.getAttribute('style');
  209.  
  210. if (tag === 'i' && typeof style === 'string' && style.indexOf('background-image') !== -1) {
  211. src = style.match(ispic)[0];
  212. } else {
  213. src = unescape(t.src);
  214. }
  215. }
  216. }
  217.  
  218. if ( ['img', 'i'].indexOf(tag) !== -1 && src.match(ispic) ) {
  219. if ( tag === 'img' && src.match(app) ) {
  220. src = src.match(ispic)[0];
  221. }
  222.  
  223. hqImg = ohoe.exists ? src.replace(XbyX, '') : hq(t, tag, src);
  224. goThroughWithShow = true;
  225.  
  226. // debug( src.match(ispic) != null ? 'src is recognized\norig url: ' + src + '\nhq url: ' + hqImg : 'src is NOT recognized' );
  227. } else if ( tag === 'div' && t.className == 'UIMediaItem_PhotoFrame' && t.parentNode.parentNode.parentNode.getAttribute('style').match(ispic) ) {
  228. hqImg = hq(t, tag);
  229. goThroughWithShow = true;
  230. }
  231.  
  232. // make sure we don't show enlarged thumbnails for album or fullscreen view images
  233. if (t.className.indexOf('spotlight') !== -1 || JSL(t).parent('#imagestage').exists) {
  234. goThroughWithShow = false;
  235. }
  236.  
  237. // if we chose to show the image, let's go through that process
  238. if (goThroughWithShow === true) {
  239. // show the image if it's indeed a facebook thumbnail
  240. // and the enlarged url is not the same as the thumbnail url
  241. show_d = window.setTimeout(show, delay, hqImg);
  242.  
  243. // let's send a quick request to see if this is a valid image
  244. // if not, we just show the original thumbnail
  245. // ps: this not only preloads the image, but it's a sure-fire way
  246. // of knowing if it's a false positive or not (e.g., status==0 but image displays fine)
  247. JSL.ajax(hqImg, {
  248. context : src,
  249. method : 'GET',
  250. onload : checkSource,
  251. onerror : checkSource
  252. });
  253. }
  254.  
  255. event.stopPropagation();
  256. }
  257.  
  258. function hidePicPop(event) {
  259. var picPop = JSL('#picPop');
  260.  
  261. // don't hide the enlarged picture if a middle or right click happens
  262. if (typeof event !== 'undefined' && typeof event.which === 'number' && event.which > 1) { return; }
  263.  
  264. window.clearTimeout(show_d);
  265.  
  266. picPop.hide();
  267. picPop.removeAttribute('src');
  268.  
  269. if (typeof event !== 'undefined') {
  270. event.stopPropagation();
  271. }
  272. }
  273.  
  274. function hq(e, tag, src) {
  275. var r = '', style = e.getAttribute('style');
  276.  
  277. switch (tag) {
  278. case 'div': {
  279. r = e.parentNode.parentNode.parentNode.getAttribute('style').match(ispic)[0];
  280. break;
  281. }
  282.  
  283. case 'img': case 'i': {
  284. if ( typeof style === 'string' && style.match(ispic) ) {
  285. r = style.match(ispic)[0];
  286. } else if (typeof src === 'string') {
  287. r = src;
  288. } else {
  289. r = e.src;
  290. }
  291.  
  292. break;
  293. }
  294. }
  295.  
  296. return r.replace(XbyX, '').replace(c, '').replace(size, '$1$2n$3');
  297. }
  298.  
  299. function info(i) {
  300. var info = JSL('#infoBox');
  301.  
  302. i = (i + '').replace(/[\n\r]/g, '\n<br />\n');
  303.  
  304. info.show('block').prop('innerHTML', i);
  305. }
  306.  
  307. // Make sure the page is not in a frame
  308. if (window.self !== window.top) { return; }
  309.  
  310. JSL.addStyle('' +
  311. '#picPop { ' +
  312. 'z-index: 99999; ' +
  313. 'position: fixed; ' +
  314. 'background: #000000; '+
  315. 'overflow: hidden; ' +
  316. 'border: 2px solid #000000; ' +
  317. 'outline: 2px solid #FFFFFF; ' +
  318. 'max-height: 98%; ' +
  319. '}\n\n' +
  320. '.HovercardOverlay, ._5uek, ._7m4, ._8xh, ._3aml, #fbProfileCover .coverBorder { ' +
  321. 'display: none !important; ' +
  322. '}' +
  323. '');
  324.  
  325. // add the info box
  326. docFrag.appendChild( JSL.create('span', {id: 'infoBox', style: 'border: 1px solid #666666; border-radius: 6px; position: fixed; top: 4px; left: 45%; font-size: 10pt; font-family: sans-serif, arial; background: #EEEEEE; color: #000000; padding: 10px; z-index: 999999; overflow: auto; display: none;'}) );
  327.  
  328. // add the hovering bigger image holder
  329. docFrag.appendChild( JSL.create('img', {id: 'picPop', style: 'display: none;', 'class':'hover_img_thumb'}) );
  330.  
  331. document.body.appendChild(docFrag);
  332.  
  333. // keep tabs on where the mouse is so we never problems with the positioning of the preview
  334. window.addEventListener('mousemove', handleMove, false);
  335.  
  336. // hover over a thumbnail
  337. window.addEventListener('mouseover', handleMouseover, false);
  338.  
  339. // hide the preview when moving the mouse off a thumbnail
  340. window.addEventListener('mouseout', hidePicPop, false);
  341.  
  342. // hide the preview when left-clicking
  343. window.addEventListener('click', hidePicPop, false);
  344.  
  345. primeThumbs();
  346. JSL.setInterval(primeThumbs, 1000);
  347.  
  348. }());