Google Search Extra Buttons

Add buttons (last 1/2/3 days, weeks, PDF search etc.) for results of Google search page

当前为 2015-10-15 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Google Search Extra Buttons
  3. // @name:ru GoogleSearchExtraButtons
  4. // @description Add buttons (last 1/2/3 days, weeks, PDF search etc.) for results of Google search page
  5. // @description:ru Кнопки вариантов поиска для результатов Google (1-2-3 дня, недели, PDF, ...)
  6. // @version 6.2015.10.15
  7. // @namespace spmbt.github.com
  8. // @include http://www.google.*/search*
  9. // @include https://www.google.*/search*
  10. // ==/UserScript==
  11.  
  12. (function(sett){ //lang, sites, lastHoursLess
  13.  
  14. var $x = function(el, h){if(h) for(var i in h) el[i] = h[i]; return el;} //===extend===
  15. ,$pd = function(ev){ev.preventDefault();}
  16. ,d = document
  17. ,$e = function(g){ //===create or use existing element=== //g={el|clone,cl,ht,cs,at,atRemove,on,apT}
  18. g.el = g.el || g.clone ||'DIV';
  19. var o = g.o = g.clone && g.clone.cloneNode && g.clone.cloneNode(!0)
  20. || (typeof g.el =='string' ? d.createElement(g.el) : g.el);
  21. if(o){ //execute if exist
  22. if(g.cl)
  23. o.className = g.cl;
  24. if(g.clAdd)
  25. o.classList.add(g.clAdd);
  26. if(g.cs)
  27. $x(o.style, g.cs);
  28. if(g.ht || g.at){
  29. var at = g.at ||{}; if(g.ht) at.innerHTML = g.ht;}
  30. if(at)
  31. for(var i in at){
  32. if(i=='innerHTML') o[i] = at[i];
  33. else o.setAttribute(i, at[i]);}
  34. if(g.atRemove)
  35. for(var i in g.atRemove)
  36. o.removeAttribute(g.atRemove[i]);
  37. if(g.on)
  38. for(var i in g.on) if(g.on[i])
  39. o.addEventListener(i, g.on[i],!1);
  40. g.ap && o.appendChild(g.ap);
  41. g.apT && g.apT.appendChild(o);
  42. }
  43. return o;
  44. },
  45. addRules = function(css){
  46. var heads = d.getElementsByTagName('head')
  47. ,node = d.createElement('style');
  48. heads.length && heads[0].appendChild(node);
  49. node.appendChild(d.createTextNode(css));
  50. };
  51. /**
  52. * check occurrence of third-party event with growing interval
  53. * @constructor
  54. * @param{Number} t start period of check
  55. * @param{Number} i number of checks
  56. * @param{Number} m multiplier of period increment
  57. * @param{Function} check event condition
  58. * @param{Function} occur event handler
  59. */
  60. var Tout = function(h){
  61. var th = this;
  62. (function(){
  63. if((h.dat = h.check() )) //wait of positive result, then occurs
  64. h.occur();
  65. else if(h.i-- >0) //next slower step
  66. th.ww = setTimeout(arguments.callee, (h.t *= h.m) );
  67. })();
  68. },
  69. setLocStor = function(name, hh){ if(!localStorage) return;
  70. localStorage['LE::xButtons_'+ name] = JSON.stringify({h: hh});},
  71. getLocStor = function(name){
  72. return (JSON.parse(localStorage && localStorage['LE::xButtons_'+ name] ||'{}')).h;}
  73. ,removeLocStor = function(name){localStorage && localStorage.removeItem('LE::xButtons_'+ name);}
  74. ,$l ={ru:{
  75. 'search in PDF files':'поиск по документам PDF'
  76. ,'search in':'искать по'
  77. ,'from / to':'за период'
  78. ,'last':['за последний','за последние','за последнюю']
  79. ,'day':'сутки'
  80. ,'days':['дня','дней']
  81. ,'week':'неделю'
  82. ,'weeks':['недели','недель']
  83. ,'month':'месяц'
  84. ,'months':['месяца','месяцев']
  85. ,'year':'год'
  86. ,'years':['года','лет']
  87. ,'hour':'час'
  88. ,'hours':['часа','часов']
  89. ,'Settings':'Настройки'
  90. ,'of userscript':'юзерскрипта'
  91. ,'reload page for effect':'перезагрузить страницу'
  92. ,'Interface language':'Язык интерфейса'
  93. ,'Sites':'Сайты'
  94. },fr:{
  95. 'search in PDF files':'la recherche dans les fichiers PDF'
  96. ,'search in':'rechercher dans'
  97. ,'from / to':'pour la période'
  98. ,'last':['le dernier','dans les derniers','dans les derniers']
  99. ,'day':'jour'
  100. ,'days':['jours','jours']
  101. ,'week':'semaine'
  102. ,'weeks':['semaines','semaines']
  103. ,'month':'mois'
  104. ,'months':['mois','mois']
  105. ,'year':'an'
  106. ,'years':['ans','ans']
  107. ,'hour':'heure'
  108. ,'hours':['heures','heures']
  109. ,'Settings':'Paramètres'
  110. ,'of userscript':'de Userscript'
  111. ,'reload page for effect':'recharger la page pour effet'
  112. ,'Interface language':'Langue de l\'interface'
  113. ,'Sites':'Les sites'
  114. },de:{
  115. 'search in PDF files':'suche in PDF-Dateien'
  116. ,'search in':'suche in'
  117. ,'from / to':'im Zeitraum'
  118. ,'last':['letzte','letzte','letzte']
  119. ,'day':'Tag'
  120. ,'days':['Tage','Tagen']
  121. ,'week':'Woche'
  122. ,'weeks':['Wochen','Wochen']
  123. ,'month':'Monat'
  124. ,'months':['Monate','Monaten']
  125. ,'year':'Jahr'
  126. ,'years':['Jahre','Jahre']
  127. ,'hour':'Stunde'
  128. ,'hours':['Stunden','Stunden']
  129. ,'Settings':'Einstellungen'
  130. ,'of userscript':'von userscript'
  131. ,'reload page for effect':'nachladen Seite für Effekt'
  132. ,'Interface language':'Schnittstellensprache'
  133. ,'Sites':'Webseiten'
  134. },es:{
  135. 'search in PDF files':'búsqueda en archivos PDF'
  136. ,'search in':'busca en'
  137. ,'from / to':'para el período'
  138. ,'last':['el último','en los últimos','en los últimos']
  139. ,'day':'día'
  140. ,'days':['días','días']
  141. ,'week':'Semana'
  142. ,'weeks':['semanas','semanas']
  143. ,'month':'mes'
  144. ,'months':['meses','meses']
  145. ,'year':'año'
  146. ,'years':['años','años']
  147. ,'hour':'hora'
  148. ,'hours':['horas','horas']
  149. ,'Settings':'Ajustes'
  150. ,'of userscript':'de userscript'
  151. ,'reload page for effect':'página para efecto de recargar'
  152. ,'Interface language':'Idioma de interfaz'
  153. ,'Sites':'Sitios'
  154. }} //if !lang, then no hints
  155. ,lNoHints ={'from / to':1, 'Settings':1, 'of userscript':1, 'reload page for effect':1, 'Interface language':1, 'Sites':1}
  156. ,lang = getLocStor('lang') || sett.lang
  157. ,sites = (getLocStor('sites') || sett.sites) instanceof Array && (getLocStor('sites') || sett.sites) ||[]
  158. ,strSites = sites.join('\n')
  159. ,$LSettings
  160. ,$L = $l[lang] || $l.ru; //default template of lang
  161. if(!lang || !$l[lang] || lang =='en') for(var l in $L){ //replace 'en' lang for default or substitution
  162. if($L[l] instanceof Array) for(var l2 in $L[l])
  163. $L[l][l2] = l;
  164. else
  165. $L[l] = l;
  166. }
  167. addRules('.siteList:hover button{display: block}'
  168. +'.gb_Ib >.gb_e{height:47px}.gb_Fb{z-index:1087}.tsf-p{z-index:203}'
  169. +'.lsbb .xButt,.lsbb >.siteList{opacity: 0.64; line-height:14px; width:34px; height:17px; padding:0 2px;'
  170. +'font-size:14px; border:1px solid transparent; background-color:#4889f1; color:#fff}'
  171. +'.lsbb >.siteList{width:32px; height:auto; padding:1px 0 2px; text-align:center}'
  172. +'.lsbb >.siteList .lsb{color:#d4d4d4}.lsbb .lsb:hover{opacity: 1; color:#fff}'
  173. +'.siteList .settIn{display: none; width: 250px; padding: 2px 4px; text-align:left; border:1px solid #48f;'
  174. +'background-color:#eef; color:#336}'
  175. +'.siteList .settIn hr{margin:2px 0}'
  176. +'.siteList .sett:hover .settIn, .siteList .settIn.changed{display: block}'
  177. +'.siteList .settIn .reload{display: none}.siteList .settIn.changed .reload{display: block}');
  178. console.log('==sites: ', getLocStor('sites'))
  179. console.log('==$L: ', $L, lang)
  180. if(sett.sites)
  181. sites.push($LSettings = $L['Settings']);
  182. new Tout({t:120, i:8, m: 1.6
  183. ,check: function(){
  184. return d && d.getElementsByName("q") && d.getElementsByName('q')[0];
  185. },
  186. occur: function(){
  187. var inputSearch = this.dat
  188. ,buttSearch = d.getElementsByName("btnG") && d.getElementsByName('btnG')[0]
  189. ,buttS ={
  190. PDF:{url:'filetype:pdf', txt:$L['search in PDF files']}
  191. ,site:{url:'site:'+ sites[0], txt:$L['search in']+' '+ sites[0], one:'day'} //you may comment this line
  192. ,'.. : ..':{url:'', txt:$L['from / to']}
  193. ,'1D':{url:'&tbs=qdr:d', txt:$L['last'][1] +' '+ $L['day'], one:'day', up:13}
  194. ,'7D':{url:'&tbs=qdr:w', txt:$L['last'][2] +' '+ $L['week'], one:'week', up:10}
  195. ,'1M':{url:'&tbs=qdr:m', txt:$L['last'][0] +' '+ $L['month'], one:'month', up:11}
  196. ,'1Y':{url:'&tbs=qdr:y', txt:$L['last'][0] +' '+ $L['year'], one:'year', up:10}
  197. ,'1H':{url:'&tbs=qdr:h', txt:$L['last'][0] +' '+ $L['hour'], one:'hour', up:23}
  198. }, ii =0;
  199. !sites && delete buttS.site;
  200. buttSearch.parentNode.style.position ='relative';
  201. if(buttSearch && top == self)
  202. for(var i in buttS) if(i !='site'|| sett.sites){ //buttons under search input line
  203. var bI = buttS[i]
  204. , butt2 = $e({clone: i =='site'|| i.length ==2
  205. ? $e({cl: 'siteList', cs: {cursor:'default'}, at: {site: sites[0], date: bI.url} })
  206. : buttSearch
  207. ,clAdd:'xButt'
  208. ,atRemove: ['id', 'name']
  209. ,at: {value: i, innerHTML: i, title: lang || i=='site'|| i=='.. : ..' ? bI.txt :''}
  210. ,cs: {position: 'absolute', top: '33px', left: (-127 + 37 * ii++) +'px'}
  211. ,on: {click: (function(bI, i){
  212. //console.log('clic:',i,bI)
  213. return /PDF|site/.test(i)
  214. ? function(ev){
  215. if(!ev.target.getAttribute('site') || ev.target.getAttribute('site')==$LSettings) return;
  216. inputSearch.value = inputSearch.value.replace(/ site\:[\w.]+$/i, '')
  217. .replace(' filetype:pdf', '') +' '
  218. + (i =='PDF' ? bI.url : 'site:'+ ev.target.getAttribute('site'));
  219. if(ev.target.className =='siteList') this.form.click();
  220. }: !bI.url ? function(ev){
  221. var el = d.querySelector('#cdrlnk'), o;
  222. el && el.dispatchEvent(((o = d.createEvent('Events')).initEvent('click', !0, !1), o));
  223. $pd(ev);
  224. }: function(ev){
  225. location.href = '/search?q='+ encodeURIComponent(inputSearch.value)
  226. +(ev.target.getAttribute('date') + ev.target.getAttribute('site').replace(/\D/g,'') || bI.url);
  227. $pd(ev);
  228. }
  229. })(bI, i),
  230. mouseover: i =='site' || i.length ==2 ? (function(bI,i){return function(ev){
  231. clearTimeout(bI.ww);
  232. ev.currentTarget.querySelector('.list').style.display ='block';
  233. }})(bI,i) :'',
  234. mouseout: i =='site' || i.length ==2 ? (function(bI,i){return function(ev){
  235. var t = ev.currentTarget;
  236. clearTimeout(bI.ww);
  237. bI.ww = setTimeout(function(){
  238. t.querySelector('.list').style.display ='none';
  239. }, 450);
  240. }})(bI,i) :'',
  241. change: function(ev){
  242. console.info('Settings are not saved. TODO save to locStor of another site.');
  243. setLocStor(ev.target.name, ev.target.type=='INPUT'&& ev.target.value
  244. || ev.target.value.replace(/^[ \n]*|[ \n]*$/g,'').split('\n'));
  245. d.querySelector('.siteList .settIn').classList.add('changed');
  246. } }
  247. ,apT: buttSearch.parentNode
  248. });
  249. bI.el = butt2;
  250. if(i =='site' || i.length ==2){ //dropdown lists under some buttons
  251. var siteList = $e({cl:'list',cs:{display:'none'}, apT: butt2}), arr =[];
  252. for(var j =0; j <= bI.up -1; j++) if(i !='1H' || !sett.lastHoursLess || j < 8 || j % 2 )
  253. arr.push((j+1) +' '+ (j % 10 || j==10 ? $L[bI.one +'s'][j % 10 <4 && (j/10|0)!=1 ?0:1] : $L[bI.one]));
  254. if(!sett.sites && i =='1H')
  255. arr.push($LSettings = $L['Settings']);
  256. var list = i == 'site' ? sites : arr;
  257. for(var j in list) if(j !=0)
  258. var sI = list[j]
  259. ,butt3 = $e({clone: sI==$LSettings
  260. ? $e({cl: 'sett lsb'})
  261. : buttSearch
  262. ,clAdd:'xButt'
  263. ,atRemove:['id','name','date']
  264. ,at:{value: sI
  265. ,site: sI
  266. ,date: bI.url
  267. ,title: sI==$LSettings || !lang ?'':(i =='site'?$L['search in']:$L['last'][1]) +' '+ sI
  268. ,innerHTML: sI +(sI != $LSettings &&!(!sett.sites && i =='1H')
  269. ?'':'<div class="settIn" title="">'
  270. +$L.Settings +' '+ $L['of userscript'] +'<br>"Google Search Extra Buttons"<hr>'
  271. +$L['Interface language'] +': <input size=4 value="'+ lang +'"/><br>' //TODO select Tag for accessible langs
  272. +$L['Sites'] +': <br><textarea style="width:97%" rows=8>'
  273. + strSites +'</textarea><br>'
  274. //+'<a class="reload" href=# onclick="location.reload();return!1">'
  275. // + $L['reload page for effect'] +'</a>'
  276. +'</div>')}
  277. ,cs: {position: sI != $LSettings ?'static':'absolute',display:'block', width:'auto', height:'18px'
  278. ,margin:'2px 0 -1px -13px', padding:0, textAlign:'left', fontWeight:'normal', opacity:1}
  279. ,apT: siteList
  280. });
  281. siteList.style.height ='auto'; siteList.style.textAlign ='center';
  282. }
  283. }
  284. $e({el:d.querySelector('.gb_Ib > .gb_e'),cs:{height:'47px'}} );
  285. $e({el:d.querySelector('.gb_Fb'),cs:{zIndex:'1087'}} );
  286. $e({el:d.querySelector('.tsf-p'),cs:{zIndex:'203'}} );
  287. }
  288. });
  289.  
  290. })({ //write "lang:''," to remove hints; 'en' for English hints (fr - Français, es - espagnol), 'ru' for Russian
  291. lang: ''|| (navigator.languages[1] || navigator.language)
  292. ,sites:['slashdot.org','engadget.com','techcrunch.com','habrahabr.ru','geektimes.ru'
  293. ,'smashingmagazine.com','maketecheasier.com'] //write your favorite sites
  294. ,lastHoursLess:1 // not show odd hours after 8 h.
  295. });
  296.  
  297.  
  298. //settings: {lang, $s ={}, lastSelects, sites}; imp/export (thru net also)