Google Images direct links 2

Add direct links to the picture to the Google Image Search results.

  1. // ==UserScript==
  2. // @name Google Images direct links 2
  3. // @version 2.5.1
  4. // @description Add direct links to the picture to the Google Image Search results.
  5. // @namespace Google
  6. // @author Benjamin Philipp <dev [at - please don't spam] benjamin-philipp.com>
  7. // @include /^https?:\/\/(www\.)?google\.[a-z\.]{2,5}\/search.*tbm=isch.*/
  8. // @include /^https?:\/\/(www\.)?google\.[a-z\.]{2,5}\/search.*udm=2.*/
  9. // @require https://openuserjs.org/src/libs/sizzle/GM_config.js
  10. // @run-at document-start
  11. // @grant GM_xmlhttpRequest
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @grant GM_registerMenuCommand
  15. // @grant GM_openInTab
  16. // @connect *
  17. // ==/UserScript==
  18.  
  19. /* eslint no-unused-vars: "off", no-implicit-globals: "off", curly: "off" */
  20. /* globals GM_config, trustedTypes */
  21.  
  22. var updateInterval = 1000;
  23. var maxtries = 100;
  24. var selector = `.rg_di.rg_bx a.rg_l img:not(.linksdone),
  25. #islrg div.isv-r a.wXeWr.islib img:not(.linksdone),
  26. div#res div#rso h3 a g-img img:not(.linksdone),
  27. div#islmp div.islrc a[role="button"] img:not(.linksdone)
  28. `;
  29. var idle = true;
  30.  
  31. GM_config.init(
  32. {
  33. 'id': 'MyConfig',
  34. 'title': GM_info.script.name + ' Settings',
  35. 'fields': {
  36. 'newTabByDefault': {
  37. 'label': '<b>Open Links in new tab by default</b> <br /><i>ON:</i> Opens images in new tab; hold <kbd>Ctrl</kbd> to open directly <br /><i>OFF:</i> Opens images directly; hold <kbd>Ctrl</kbd> to open in new tab',
  38. 'type': 'checkbox',
  39. 'default': true
  40. },
  41. 'openInBackground': {
  42. 'label': '<b>Open new tabs in the background (without activating them)</b>',
  43. 'type': 'checkbox',
  44. 'default': false
  45. },
  46. 'uncropImages': {
  47. 'label': '<b>Uncrop images</b> <br /><i>Google crops most preview images slightly. Turning this setting on will show the thumbnails uncropped</i>',
  48. 'type': 'checkbox',
  49. 'default': false
  50. },
  51. 'uncropOnHover': {
  52. 'label': '<b>Uncrop on hover</b> <br /><i>Show the full thumbnail on mouse hover</i>',
  53. 'type': 'checkbox',
  54. 'default': true
  55. },
  56. 'noRadius': {
  57. 'label': '<b>Remove round corners</b> <br /><i>remove the "rounded corners" (CSS: border-radius) effect. Some thumbnails seem to have actually rounded corners baked in, we can\'t do anything about those</i>',
  58. 'type': 'checkbox',
  59. 'default': true
  60. }
  61. },
  62. 'css': `
  63. #MyConfig .field_label{
  64. font-weight: normal;
  65. font-size: 13px;
  66. }
  67. #MyConfig kbd{
  68. border: 1px solid #ccc;
  69. background: #eee;
  70. border-radius: 3px;
  71. padding: 1px 3px;
  72. font-family: consolas, monospace;
  73. }`,
  74. 'events': {
  75. 'init': setClasses,
  76. 'save': setClasses
  77. }
  78. });
  79.  
  80. function setClasses(){
  81. console.log(document.body);
  82. document.body.classList.toggle("nocrop", GM_config.get("uncropImages"));
  83. document.body.classList.toggle("nocropHover", GM_config.get("uncropOnHover"));
  84. document.body.classList.toggle("noRadius", GM_config.get("noRadius"));
  85. // console.log(GM_config.get("uncropImages"));
  86. // console.log(GM_config.get("uncropOnHover"));
  87. // console.log(GM_config.get("noRadius"));
  88. // console.log(document.body.classList);
  89. }
  90.  
  91. GM_registerMenuCommand(GM_info.script.name + ' Settings', function(){
  92. GM_config.open();
  93. });
  94.  
  95. // Custom TrustedTypes handling: Google's policies are giving us trouble in some configs.
  96. var needsTrustedHTML = false;
  97. var passThroughFunc = function(string, sink) {
  98. return string;
  99. };
  100. var TTPName = "toast";
  101. var TP = {createHTML: passThroughFunc, createScript: passThroughFunc, createScriptURL: passThroughFunc};
  102. try{
  103. if(typeof window.isSecureContext !== 'undefined' && window.isSecureContext){
  104. if (window.trustedTypes && window.trustedTypes.createPolicy){
  105. if(trustedTypes.defaultPolicy){
  106. console.log("TT Default Policy exists");
  107. TP = trustedTypes.defaultPolicy; // Is the default policy permissive enough? If it already exists, best not to overwrite it
  108. }
  109. else{
  110. TP = window.trustedTypes.createPolicy(TTPName, TP);
  111. }
  112. console.log("TP is now", TP);
  113. needsTrustedHTML = true;
  114. }
  115. else{
  116. console.log("Uh-oh");
  117. }
  118. }
  119. }catch(e){
  120. console.log(e);
  121. }
  122. function updatePage()
  123. {
  124. if(document.querySelector("#directLinkStyles") == null){
  125. // console.log("TP:", TP);
  126. let c = document.createElement("STYLE");
  127. c.id = "directLinkStyles";
  128. c.innerHTML = trustedHTML(`
  129. .linkToTarget{
  130. box-shadow: 3px 5px 10px rgba(0,0,0,0.5);
  131. cursor: default;
  132. position: absolute;
  133. right:0; top:0;
  134. opacity: 0;
  135. background-color: rgba(255,255,255,0.5);
  136. transition: background-color 0.5s, opacity 0.5s
  137. }
  138. .failed .linkToTargetlink{
  139. color: rgba(230,100,100)!important;
  140. }
  141. a:hover .linkToTarget{
  142. opacity: 0.6;
  143. }
  144. a:hover .linkToTarget:hover{
  145. opacity: 1;
  146. }
  147. .linksdone:hover .linkToTarget{
  148. cursor: pointer;
  149. }
  150. .linkToTargetLink{
  151. color: rgba(155,177,233, 1)!important;
  152. font-size: 22pt;
  153. display: block;
  154. font-weight: bold;
  155. text-decoration: none!important;
  156. transition: color 0.5s, font-size 0.5s, padding 0.5s;
  157. }
  158. .temp .linkToTargetLink{
  159. color: rgba(200,200,200)!important;
  160. }
  161. .linkToTargetLink:hover{
  162. color: rgba(155,177,233, 1)!important;
  163. padding:8px;
  164. font-size: 30pt;
  165. }
  166. body.nocrop div#islmp div#islrg div.islrc div.isv-r a.islib,
  167. body.nocrop a .F0uyec,
  168. body.nocrop a img,
  169. body.nocrop .mNsIhb .YQ4gaf,
  170. body.nocrop .H8Rx8c img,
  171. body.nocropHover div#islmp div#islrg div.islrc div.isv-r a.islib:hover,
  172. body.nocropHover a .F0uyec:hover,
  173. body.nocropHover a:hover img,
  174. body.nocropHover .mNsIhb .YQ4gaf:hover,
  175. body.nocropHover .H8Rx8c img:hover{
  176. overflow: visible;
  177. z-index: 100;
  178. object-fit: contain;
  179. }
  180. body.noRadius a .F0uyec,
  181. body.noRadius div .eA0Zlc.mkpRId,
  182. body.noRadius div .cC9Rib{
  183. border-radius: 0;
  184. }
  185. </style>`);
  186. document.querySelector("head").appendChild(c);
  187. }
  188. document.querySelectorAll(selector).forEach(function(e){
  189. if(e.classList.contains("linksdone")) // Why is the selector not working??
  190. return;
  191. var c = document.createElement("DIV");
  192. c.className="linkToTarget";
  193. c.innerHTML = trustedHTML("<a class='linkToTargetLink'>↗️</a>");
  194. e.parentElement.appendChild(c);
  195. c.querySelector("a.linkToTargetLink").onclick = clickLink;
  196. e.classList.add("linksdone");
  197. });
  198. }
  199.  
  200. function clickLink(e){
  201. e.stopPropagation();
  202. e.preventDefault();
  203. var t = e.target;
  204. waitForLink(t, e);
  205. return false;
  206. }
  207.  
  208. function waitForLink(t, e){
  209. var tp = t.parentElement.closest("a");
  210. console.log(tp);
  211. var imin = tp.href.indexOf("imgurl=");
  212. var openInNew = e.which==2;
  213. if(GM_config.get("newTabByDefault")){
  214. if(!e.ctrlKey)
  215. openInNew = true;
  216. }
  217. else{
  218. if(e.ctrlKey)
  219. openInNew = true;
  220. }
  221. if(imin<0)
  222. {
  223. var $e = tp;
  224. var restries = tp.getAttribute("resTries")?tp.getAttribute("resTries")*1+1:1;
  225. if(restries==1){
  226. $e.click();
  227. // tp.querySelector("img")?.click();
  228. tp.querySelector("img").click();
  229. setTimeout(function(){
  230. $e.click();
  231. }, 200);
  232. // $(tp).find("img").contextmenu();
  233. // $(tp).trigger({
  234. // type: 'mousedown',
  235. // which: 2
  236. // });
  237. // waitfor("#islsp a[aria-label='Close']", function(o){
  238. // $(o).click(); // somehow doesn't close the details view either
  239. // });
  240. }
  241. // #Sva75c > div.A8mJGd.NDuZHe.OGftbe-N7Eqid-H9tDt > div.LrPjRb > div.AQyBn > div.tvh9oe.BIB1wf > c-wiz > div > div > div > div > div.v6bUne.qmmlRd > div.p7sI2.PUxBg > a > img.sFlh5c.pT0Scc.iPVvYb
  242. // console.log("try", restries);
  243. tp.setAttribute("resTries", restries);
  244.  
  245. if(tp.getAttribute("resTries")*1>=maxtries){
  246. console.log("This Link won't come up with a good fragment: " + tp.querySelector("img").src);
  247. tp.classList.add("linksdone");
  248. tp.classList.add("failed");
  249. tp.querySelector(".linkToTarget span").innerHTML = TP.createHTML("x");
  250. return true;
  251. }
  252. if(!tp.classList.contains("linkswait")){
  253. tp.classList.add("linkswait");
  254. tp.querySelector(".linkToTarget").classList.add("temp");
  255. tp.querySelector(".linkToTarget span").innerHTML = TP.createHTML("...");
  256. }
  257. // console.log("Not ready");
  258. setTimeout(function(){
  259. console.log("try again");
  260. waitForLink(t, e);
  261. }, 200);
  262. return true;
  263. }
  264. else{
  265. console.log("got link");
  266. var linkconts = tp.href.substr(imin+7);
  267. var piclink = linkconts.substr(0,linkconts.indexOf("&"));
  268. var reflink = linkconts.substr(linkconts.indexOf("imgrefurl=")+10);
  269. reflink = decodeURIComponent(reflink.substr(0, reflink.indexOf("&")));
  270. piclink = decodeURIComponent(piclink);
  271. tp.classList.remove("linkswait");
  272. let tl = tp.querySelector(".linkToTarget");
  273. if(tl){
  274. tl.classList.remove("temp");
  275. tl.querySelector("a.linkToTargetLink").href = piclink;
  276. }
  277. else
  278. console.log("Link not found?", tp);
  279. tp.classList.add("linksdone");
  280. if(e.which == 3)
  281. return false; // Don't open new tab on right click
  282. // console.log("Background?", GM_config.get("openInBackground"));
  283. if(openInNew){
  284. GM_openInTab(piclink, {
  285. active: !GM_config.get("openInBackground"),
  286. insert: true,
  287. parent: true
  288. });
  289. }
  290. else{
  291. location.href = piclink;
  292. }
  293. }
  294. }
  295.  
  296. function trustedHTML(string) {
  297. if (!needsTrustedHTML)
  298. return string;
  299. const TT = TP.createHTML(string);
  300. // console.log(typeof TT, TT);
  301. return TT;
  302. }
  303.  
  304. setInterval(updatePage, updateInterval);
  305.  
  306. updatePage();