├── .gitignore ├── README.md └── direct_google_images.user.js /.gitignore: -------------------------------------------------------------------------------- 1 | /.settings/ 2 | /.project 3 | /.tern-project 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Direct Google Images 2 | **License:** [GPLv2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) or later 3 | 4 | **Install:** [Stable](https://rawgit.com/zanetu/direct_google_images/master/direct_google_images.user.js) / [Beta](https://rawgit.com/zanetu/direct_google_images/beta/direct_google_images.user.js) 5 | 6 | #### A userscript that provides direct links in Google Images. 7 | - Includes some enhancements as to images embedded in web search results. 8 | - Can be used in conjunction with [Direct Google](http://github.com/zanetu/direct_google). 9 | -------------------------------------------------------------------------------- /direct_google_images.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Direct Google Images 3 | // @namespace http://greasyfork.org/en/users/461 4 | // @version 0.99 5 | // @description Provides direct links in Google Images. 6 | // @include /^https?\:\/\/(www|encrypted)\.google\./ 7 | // @author zanetu 8 | // @license GPL version 2 or any later version; http://www.gnu.org/licenses/gpl-2.0.txt 9 | // @grant GM_addStyle 10 | // @run-at document-start 11 | // @noframes 12 | // ==/UserScript== 13 | 14 | //do not run in frames or iframes 15 | if(window.top == window.self) { 16 | var RE = /imgres\?imgurl\=(http.+?)\&imgrefurl\=(http.+?)(\&|$)/i 17 | var RE_SOURCE = /url\?(?:.*?\&)*?url\=(http.+?)(\&|$)/i 18 | var WATCH_EVENTS = ['mouseenter', 'mousedown', 'click', 'focus', 'touchstart'] 19 | var clickCount = 0 20 | 21 | function dd(url) { 22 | var d1 = decodeURIComponent(url), d2 23 | try { 24 | d2 = decodeURIComponent(d1) 25 | } 26 | catch(malformed) { 27 | return d1 28 | } 29 | return d2 30 | } 31 | 32 | function closest(element, matchFunction, maxLevel) { 33 | var max = undefined === maxLevel ? Number.POSITIVE_INFINITY : parseInt(maxLevel) + 1 34 | if(max > 0 && 'function' === typeof matchFunction) { 35 | for(; element && max--; element = element.parentNode) { 36 | if(matchFunction(element)) { 37 | return element 38 | } 39 | } 40 | } 41 | return null 42 | } 43 | 44 | function modifyGoogleImage(element) { 45 | if(element && element.href) { 46 | var m = element.href.match(RE) 47 | if(m && m[1] && m[2]) { 48 | element.href = dd(m[1]) 49 | setDirect(element) 50 | if(isClickedLast(element)) element.click() 51 | return true 52 | } 53 | m = element.href.match(RE_SOURCE) 54 | if(m && m[1]) { 55 | element.href = dd(m[1]) 56 | setDirect(element) 57 | return true 58 | } 59 | } 60 | return false 61 | } 62 | 63 | function isDirect(e) { 64 | return 'yes' === (e && e.getAttribute && e.getAttribute('direct')) 65 | } 66 | 67 | function setDirect(e) { 68 | e && e.setAttribute && e.setAttribute('direct', 'yes') 69 | } 70 | 71 | function triggerMouseEvent(element, eventType) { 72 | var event = new MouseEvent('mousedown', { 73 | bubbles: true, 74 | cancelable: true 75 | }) 76 | element.dispatchEvent(event) 77 | } 78 | 79 | function isClickedLast(e) { 80 | return clickCount == (e && e.getAttribute && e.getAttribute('click-number')) 81 | } 82 | 83 | function cacheClick(e) { 84 | e && e.setAttribute && e.setAttribute('click-number', ++clickCount) 85 | } 86 | 87 | //override event handlers 88 | for(var i in WATCH_EVENTS) { 89 | document.addEventListener(WATCH_EVENTS[i], function(event) { 90 | var t = event.target 91 | var aContainer = closest(t, function(e) { 92 | return 'A' === e.nodeName 93 | && ( 94 | //image; regex can be replaced by more recent classList.contains() 95 | /(^|\s)islib(\s|$)/.test(e.className) 96 | //image source 97 | || 'noopener' === e.getAttribute('rel') 98 | ) 99 | }, 2) 100 | if(!aContainer) return 101 | if('click' === event.type) { 102 | //suppress unwanted side bar 103 | event.stopPropagation() 104 | //cache left clicks, ignoring keys like ctrl 105 | if(0 === event.button) cacheClick(aContainer) 106 | } 107 | if(isDirect(aContainer)) { 108 | if('mouseenter' !== event.type) event.stopPropagation() 109 | return 110 | } 111 | if('mouseenter' === event.type) { 112 | if(!modifyGoogleImage(aContainer)) { 113 | var observer = new MutationObserver(function(mutationRecords) { 114 | for(var j in mutationRecords) { 115 | modifyGoogleImage(mutationRecords[j].target) 116 | } 117 | }) 118 | observer.observe(aContainer, {attributes: true}) 119 | triggerMouseEvent(t, 'mousedown') 120 | } 121 | } 122 | }, true) 123 | } 124 | } 125 | --------------------------------------------------------------------------------