(function(win, u){var alienFrame = /(plusone\.google\.com|spmbt\.github\.io)/.test(location.host) ,metaTx = !alienFrame && function(s){return(s= //если Firefox+GreaseMonkey, требуется удалить "/*" перед "; // @resource meta habrAjax.meta.js // @icon data:image/gif;base64,R0lGODlhIAAgAMMBAG6Wyv///2+NtIucstfY2b/FzpSmvY+QkM3Nzunp6fLy8qGwweDg4MbFxa2trrm6uiwAAAAAIAAgAAAE/xDISau9OM/AOe2edoHBBwqiRZodmrKhRLqXYFfrdmLCQBQGWk62swgOiERAQQgChs9iRZBMKDgEFGnbMi4YDMU1gNBytzSJDcGwXhUD4lmqZofFioZrPqMIDARtYksIAzZ8dAINgngJVgkLUH1qBmBuCgmBYA6SUgKBl0wICA6lk1FdAAIFjngKDAgEpKYgWXIcKH8EDQ0EVwmjsrycIA4FZl2rDwcHDgivow8ODwzEHca3ASgDpMylsrEOzdUkDk59AtOl07wIDcwNkDbzCy7z8xIDD8Ps3Q5hCQqscxBHgw0DbEY1WIbEkRtHZV6oMsAq0wNqrcQ4KihR1Z9YjzUeKjjWcYqABUoaJeBY0k8bAm5ItqxgANjFBnBmTgnTQNw0nVOSNBjQLA1QXdEMATVioGnJCAA7 // @downloadURL none // ==/UserScript== */s//]]> )} // © licensed by LGPLv3 Open Source www.gnu.org/licenses/lgpl-3.0.en.html ,isFxScr = typeof GM_getMetadata !=u //-Scriptish ,readMeta = function(s, isFxScr){ //парсинг многострочного текста по мета-директивам if(typeof s !='string') //очистка оболочки функций, выделение мн-стр-комментария s = typeof s=='function' ? ((/\*/.test(function(){/**/}+1) ? s : s(!1) )+'') .replace(/(^[\s\S]*\*\/\/\*\r?\n?|\r?\n?\*\/s[\s\S]*$)/gm,'') : (typeof s !=u && s!==null && s.toString ? s.toString() :''); //здесь же- 'xml' var metaD ={}, j =0; if(s==='false'&& isFxScr){ //получать ли данные средствами Scriptish metaD = GM_getMetadata(); for(var i in metaD){ //приведение к нормальному виду if(metaD[i].length ==1) metaD[i] = metaD[i][0]; j++; } }else{ var meta = s.split('\n'), aa, a2; for(var i=0, mL = meta.length; i < mL; i++){ if(( aa = /^.*?\/\/\s*@([\S]+)\s(\s*)(.*)/g.exec(meta[i]) )){ a2 = aa[3] !==undefined && aa[3] || aa[2]; if(metaD[aa[1]]===undefined) metaD[aa[1]] = a2; else{ if(! (metaD[aa[1]] instanceof Array)) metaD[aa[1]] = [metaD[aa[1]]]; metaD[aa[1]].push(a2); } j++; }else if(!/^.*?\/\/\s*[\-=]*\s*\/?\s*UserScript\s*[\-=]*\s*$/i.test(meta[i])) metaD[j++] = meta[i]; } } return j >1 && metaD || undefined; //хеш директив + нум.список простых строк + _length -чис.простых строк или und., если не найдено }, extMeta = function(m, callback, callErr){ //или (url, callback, callErr)//получение внешних метаданных if(typeof m =='string') var shortNum = (/^\d+(\.meta\.js)?$/.test(m) ? m :'') + (/^\d+$/.test(m) ?'meta.js':'') ,xUrl = shortNum ? URLSCR + HAJAX +'/code'+ shortNum : m ; //wcl(m, shortNum, xUrl) if(typeof GM_xmlhttpRequest !=u){ GM_xmlhttpRequest({ url: xUrl ,method:'get' //Chrome ,onload: callback //параметр - req (для req.responseText) ,onerror: callErr //параметр - req (для req.responseText) }); }else if(win.opera && xUrl){ //load metadata for Opera var ifr = document.createElement('iframe'); ifr.src = xUrl; ifr.style.display ='none'; document.body.appendChild(ifr); ifr.name = ifr.id ='operaEmbedMeta'; win.addEventListener('msg', function(ev){ //слушать приход Event callback({responseText: ev.data.replace(/\n?\noperaEmbedMeta$/,''), finalUrl: xUrl}); },!1); }else wcl('~~No read metadata!'); //другой способ запроса внешнего сервера по ajax return {waitHandler: 0 //для немедленного таймаута ,callback: callback, callErr: callErr}; //для немедленного завершения }; win = (function(){return this})(); var DAY = 86400000 ,CHKUPD = 15 //мин. период проверок обновлений скрипта (минут) при ошибках чтения ,NOWdate = new Date() ,NOW = +NOWdate,HSO='http://habrastorage.org',SHRU='https://habrahabr.ru/auth' ,HRU ='http://habrahabr.ru',sHQ='habr.statis.tk/c?id=@&in=@&zc=@&at=@' //37.230.115.43исп-ть ли сервер статистики ,HRs ={ha: HRU, geek:'http://geektimes.ru',manag:'http://megamozg.ru',qa:'http://toster.ru',haru:'http://haru'} ,HClons ={soha:'sohabr',sape:'savepearlharbor',haru:'haru'} ,ROOT = location.protocol +'//'+ location.host ,URLSCR ='https://greasyfork.org/scripts/' ,URLCSS ='http://userstyles.org/styles/33690/' ,HAJAX ='1970-habrajax/' ,userNameMaxLen = 25 ,isFx = /Firefox/.test(navigator.userAgent) ,isChrome = /Chrome\//.test(navigator.userAgent) ,wcl = function(a){ a = a!==undefined ? a :''; //консоль как метод строки или функция, с отключением по hS.noConsole.val ==1 if(win.console && typeof hS !=u && !hS.noConsole.val) win.console.log.apply(console, this instanceof String ? ["'=="+ this +"'"].concat([].slice.call(arguments)) : arguments); } , strongCutImgMinH; String.prototype.wcl = wcl; String.prototype.trim = function(s){var s = this ||s; return s.replace(/(^\s+|\s+$)/g,'')}; if(win.opera || typeof GM_xmlhttpRequest ==u) GM_xmlhttpRequest = function(h){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ var responseState ={ responseXML: xhr.readyState==4 ? xhr.responseXML :'' ,responseText: xhr.readyState==4 ? xhr.responseText :'' ,readyState: xhr.readyState ,responseHeaders: xhr.readyState==4 ? xhr.getAllResponseHeaders() :'' ,status: xhr.readyState==4 ? xhr.status : 0 ,statusText: xhr.readyState==4 ? xhr.statusText :'' }; h.onreadystatechange && h.onreadystatechange(responseState); if(xhr.readyState==4){ if(h.onload && xhr.status>=200 && xhr.status<300) h.onload(responseState); if(h.onerror && (xhr.status<200 || xhr.status>=300)) h.onerror(responseState); }}; try{//cannot do cross domain xhr.open(h.method, h.url); }catch(er){ if(h.onerror) //simulate a real error h.onerror({responseXML:'',responseText:'',readyState:4,responseHeaders:'',status:403,statusText:'Forbidden'}); return; } if(h.headers) for(var prop in h.headers) xhr.setRequestHeader(prop, h.headers[prop]); xhr.send((typeof(h.data) !=u) ? h.data : null); }; try{ //для оповещения об ошибках в Fx var metaD = readMeta(metaTx, isFxScr) //metaTx == false ||'false'(строка) - если Fx|| строки метаданных ,gPlusFrame = alienFrame && /plusone/.test(location.host); win.adriver = function(){}; win.adriver.domReadyQueue ={execute: function(){},Plugin: function(){this.a =1;}}; //=============================================== //'testUScr'.wcl({isFxScr:isFxScr, unsafeWindow: typeof unsafeWindow !=u}); //win !==window при Greasemonkey|Scriptish if(!alienFrame){ //alert(addEventListener) win.addEventListener('message', function(ev){ //слушать приход данных //'start_parse'.wcl(win.JSON , win.JSON.parse, win.JSON.decode) if(win.JSON && !win.JSON.parse && win.JSON.decode) win.JSON.parse = win.JSON.decode; //habr - old //опера требует этого внутри хендлера //'decode'.wcl(win.JSON , !win.JSON.parse , win.JSON.decode, win.JSON && !win.JSON.parse); if(/likes/.test(ev.data) && /frme/.test(ev.data) && win.JSON && win.JSON.parse){ //поставить лайки снаружи var n = JSON.parse(ev.data); if(n){ //'parsed_data'.wcl(n); var frs = document.getElementsByTagName('iframe'); for(var i in frs) if(frs[i].name == n.frme){ frs[i].setAttribute('likes', n.likes); var x = frs[i].parentNode.parentNode.querySelector('.likes div div'); x && (x.innerHTML = n.likes); } } } if(/\noperaEmbedMeta$/.test(ev.data) && win.JSON && win.JSON.parse){ //alert(ev.data) var evt = document.createEvent('Event'); //генерировать для перехвата на приёме файла evt.initEvent('msg',!0,!0); evt.data = ev.data; win.dispatchEvent(evt);} },!1); } if(gPlusFrame){ /** * evaluate script in window scope * @param{Function} fs function or string is body of function * @param{String|Array} s string or array of strings for arguments * @param{Boolean} noOnce not delete script after exec */ var winEval = function(fs, s, noOnce){ //exec function/text in other scope s = (s ||[]) instanceof Array? s ||[] : [s]; //wrap by array alert(s +' '+ 'fs2') var fs2 = typeof fs=='function' ? (fs +'').replace(/(^\s*function\s*\([^\)]*\)\s*\{\s*|\s*\}\s*$)/g,'') //clean wrapper : fs ,as =''; for(var i =0, sL =s.length; i < sL; i++) //sequential array as += (i?',':'') +"'"+ s[i].replace(/'/g,"\\'").replace(/(\r\n|\r|\n)/g,"\\\n") +"'"; fs = '(function(){'+ fs2 +'}).apply(window,['+ as +']);'; //'fs'.wcl(fs, fs2) var d = document ,scr = d.createElement('script'); scr.setAttribute('type','application/javascript'); scr.textContent = fs; var dPlace = d.body || d.getElementsByTagName('head') && d.getElementsByTagName('head')[0]; dPlace.appendChild(scr); if(!noOnce) dPlace.removeChild(scr); }; /** * check occurrence of third-party event with growing interval * @constructor * @param{Number} t start period of check * @param{Number} i number of checks * @param{Number} m multiplier of period increment * @param{Function} check event condition * @param{Function} occur event handler */ var Tout = function(h){ var th = this; (function(){ if((h.dat = h.check() )) //wait of positive result, then occcurense h.occur(); else if(h.i-- >0) //next slower step th.ww = win.setTimeout(arguments.callee, (h.t *= h.m) ); })(); }; new Tout({t:320, i:6, m: 1.6 ,check: function(){ return document && document.querySelector('#aggregateCount'); } ,occur: function(){ var id = location.hash.match(/(\?|#|&)id=([^&]+)/) //frame id [or name] ,w = win; id = id && id.length && id[2]; var s = w.JSON && w.JSON.stringify && w.JSON.stringify( //must supported earlier {likes: this.dat.innerHTML, frme: id}) //data format ,pHost = (function(a){ //host extract from parameter (#|&)parent if(!a.match(/^https?\:\/\//)) return''; var b = document.createElement('a'); b.href = a; b.pathname = b.search = b.hash =''; return b.href.replace(/\/\??\#?$/,'') })( decodeURIComponent( (w.location.href.match(/.*(\?|#|&)parent=([^&]+)/) ||[])[2] ||'') ); try{ if(!isChrome || w.parent && w.parent.postMessage){ s && w.parent.postMessage(s, pHost); //all browsers except Chrome }else if(s) winEval(function(args){ var w = window ,p1 = arguments[0] ,p2 = arguments[1]; if(w.postMessage && p1 && w != w.parent){ function wpm(){ w.parent.postMessage(p1, p2); //msg with a glance Chrome bug } w.document.all ? w.setTimeout(wpm, 0) : wpm(); } }, [s, pHost]); }catch(er){wcl(er)} } }); }else if(alienFrame){ // для Оперы (только) в фрейме greasyfork.org - отправка метаданных document.addEventListener('DOMContentLoaded',function(){ var dd = document.querySelector('pre') ,s = dd && dd.innerHTML; if(s && win.parent && win.parent.postMessage) win.parent.postMessage(s +'\noperaEmbedMeta', ROOT); },!1); } //(далее выполняется, если не в alienFrame) if(alienFrame) return; var setLocStor = function(name, hh){ if(!localStorage) return; localStorage['habrAjax_'+ name] = JSON.stringify({h: hh}); }, getLocStor = function(name){ return (JSON.parse(localStorage && localStorage['habrAjax_'+ name] ||'{}')).h; } ,removeLocStor = function(name){localStorage.removeItem('habrAjax_'+ name);} ,lh = location.href ,$q = function(q, f, f2, args){ // контекстный DOM-селектор или условная функция с ним: (elem)q | ((str)q, f, args) | ((str)q, elem) | ((str)q, (elem)context, f, args) var Q = q && q.attributes && q || (!(f instanceof Function) && f||document).querySelector(q); return f instanceof Function ? f && Q ? f.apply(Q, f2 instanceof Array && f2 || [f2]) : Q : f2 instanceof Function ? f2 && Q ? f2.apply(Q, args instanceof Array && args || [args]) : Q : Q } ,$qA = function(q, f, f2, args){ var Q = q && q.attributes && q || (!(f instanceof Function) && f||document).querySelectorAll(q); return f instanceof Function ? f && Q.length ? f.apply(Q, f2 instanceof Array && f2 || [f2]) : Q : f2 instanceof Function ? f2 && Q.length ? f2.apply(Q, args instanceof Array && args || [args]) : Q : Q } previButt = $q('#post_form .buttons input[name="preview"]') wwPrevi =0; previButt && previButt.addEventListener('click',function(ev){ wcl('previclick'); win.clearTimeout(wwPrevi); textContentPrev ='00'; fillLetter();$pd(ev); },!1); var textContentPrev ='00' ,fillLetter = function(){ //сформировать письмо в ЛС var aLHash = lh.replace(/^[^#]*#/,'').split('&') ,hLHash ={} ,lStor = getLocStor('composeLetter') ,lStNoBq = lStor && !/<\/?blockquote>/.test(lStor.cite); if(/\/(add|edit)\//.test(lh)){ //страница редактирования (своей) статьи - по цитате var ta = $q('#text_textarea') ,cite = lStor && lStor.cite ,text = lStor && lStor.text ,textContentDiv = $q('#preview_placeholder .content'); if(textContentDiv) var textContent = textContentDiv.innerHTML; //содержимое статьи if(ta && typeof cite !=u){ var tVal = ta.value ,iS =[] ,cL = cite.length; for(var i =0, tL = tVal.length; i < tL; i++) if(tVal.substr(i, cL) == cite) iS.push(i); //все индексы вхождения строк 'cite'.wcl(iS, cite) if(iS.length){ if(iS.length >1) //выделить найденное hN && hN.addNote(nSufRu(iS.length, ['образ','ец','ца','цов'])+' для правки; следующий',0,0,function(){ var c1 = $q('a', this.o); c1.addEventListener('click', function(ev){ var i = !c1.getAttribute('data-curr') ? 1: +c1.getAttribute('data-curr') ,a = JSON.parse(c1.getAttribute('data-match') ) ,cite = c1.getAttribute('data-patrn'); ta && ta.setSelectionRange && ta.setSelectionRange(a[i], a[i] + cite.length); c1.setAttribute('data-curr', (++i) % a.length); ta.blur(); ta.focus(); $pd(ev); },!1); }); var i1 = iS[0]; 'citeEnd0'.wcl(nSufRu(iS.length, ['образ','ец','ца','цов']) ) ta && ta.setSelectionRange && ta.setSelectionRange(i1, i1 + cL); 'citeEnd'.wcl(iS, cite) }else hN && hN.addNote('не найдено образцов для правки'); 'citeEnd1'.wcl(iS, cite) ta.focus(); removeLocStor('composeLetter'); } if(textContentPrev !='00'&& textContent != textContentPrev){ //обработать подгруженное 'renderOfAvaxTxt'.wcl(textContentPrev && textContentPrev.length, textContent && textContent.length) authorClicks(textContentDiv); //особые клики по авторам (напр. в фрейм) //blockBrs(textContentDiv); //сокращение верт.зазоров extLinks(textContentDiv); //ext.links и подписи к старым местным линкам handlImgViews(textContentDiv); //images byTextNodes(textContentDiv, haReplace); // --> XX //обработка выделения текста }else wwPrevi = win.setTimeout(fillLetter, 1499); //проверка обновлений cite if(textContentPrev =='00') textContentPrev = textContent; return; } 'conversations'.wcl(lStor, hLHash, lStNoBq, aLHash.length); if(aLHash[0] ==lh) aLHash =[]; for(var i =0; i < aLHash.length; i++){ var a = aLHash[i].split('='); hLHash[a[1]!==undefined && a[0]] = decodeURIComponent(a[1]!==undefined && a[1] || a[0]); } var fieldTo = $q('.conversation_page input[name="users-suggest"]') ,fieldTheme ='Тема: ' ,fieldText = $q('#text_textarea'); 'fieldTo'.wcl(fieldTo, fieldTheme, fieldText); fieldTo &&(fieldTo.value = hLHash.to && hLHash.to.trim() || lStor && lStor.to ||''); fieldTheme += hLHash.subj && hLHash.subj.trim() || (lStor ?(lStor.url ?'':'') + lStor.subj +(lStor.url ?'':''):'') +(!hLHash.subj && lStor && lStor.date && lStor.subj ?' ('+ lStor.date +')':''); if(fieldText) fieldText.value = (fieldTheme ? fieldTheme +'\n':'') + (aLHash.length && !(/^comment_\d+$/.test(aLHash[0])) && ( //по URL-параметрам hLHash.cite && ('
'+ hLHash.cite +'') ||' ' )|| (lStor ?( //по хранилищу, если нет URL-параметров (lStor.commId ?' # '+ lStor.commId +' ' +'@'+ lStor.commAuthor +'\n':'') +(lStNoBq?'
':'')+ (lStor.cite ||'') +(lStor.commDate?' ('+lStor.commDate+')':'') +(lStNoBq?'':'') +(lStor.text ? lStor.text :'') ):'') ); 'lStor.cite'.wcl(lStor && '|'+lStor.cite+'|'+lStor.commAuthor) if(lStor && !aLHash.length && !lStor.noReceiver) removeLocStor('composeLetter'); if(lStor && lStor.noReceiver){ if(lStor && lStor.noReceiver ==2) delete lStor.noReceiver; 'lStor'.wcl(lStor) lStor.noReceiver =2; setLocStor('composeLetter', lStor); } if(fieldTo) fieldTo.focus(),fieldTo.select(); //выделен адресат для быстрой смены if(fieldText){ fieldText.focus(); //в поле ввода текста /*var evt = document.createEvent("KeyEvents"); evt.initKeyEvent('keyup',!0,!0, win,!1,!1,!1,!1, 40,0); fieldText.dispatchEvent(evt);*/ } }, nSufRu = function(n, vocab){ //числительные склонения слов ("день" и др.) var an = Math.abs(n) ,vocab = typeof vocab =='number'? [['д','ень','ня','ней']][vocab] : vocab; return n +' '+ vocab[0] + (an % 10 >0 && an % 10 <5 && Math.floor(an % 100 / 10) !=1 ? (an % 10 ==1 ? vocab[1] : vocab[2]) : vocab[3]); }; //===begin from Dollchan Extension Tools=== --группа функций просмотра изображений-- var doc = win.document ,$pd = function(ev){ev.preventDefault();} ,$sp = function(ev){ev.stopPropagation();},$pdsp = function(ev){ev.preventDefault();ev.stopPropagation();} ,$dispTogl = function(el){el.style.display = el.style.display ==='none'?'':'none'; return el;} ,$offset = function(el){ var box = el.getBoundingClientRect(), aa; 'offset'.wcl(aa={ top: Math.round(box.top + win.pageYOffset), left: Math.round(box.left + win.pageXOffset) }) return aa; }, addFullImg = function(a, sz, x2, isExp){ //построение изображения по клику на ссылке x2 = x2 || 1; var newW ='' ,newH ='' ,fullW = x2* sz[0] ,fullH = x2* sz[1] ,scrW = doc.documentElement.clientWidth -10 ,scrH = win.innerHeight -3 ,isA = a.tagName =='A' ,full = isA ? $q('.de-img-full', a) : a.nextSibling; if(full && /de-img-full/.test(full.className) && isExp || !full && isExp === false) return; if(hS.viewImgs && hS.viewImgs.val && !hS.viewImgCenter.val && !$q('img[style*="fixed"]', a) ) $dispTogl($q('img', a)); //TODO зкспериментально; отключено if(full && /de-img-full/.test(full.className)){ if(full.moved) full.moved = false; else{ $dispTogl(full); setTimeout(function(){full.parentNode.removeChild(full);},1); } return; } if(hS.viewImgs && hS.viewImgs.val && !hS.viewImgCenter.val) scrW -= $offset(a).left + 25; //TODO зкспериментально; отключено else{ var el = $q('.de-img-center', doc); el && el.parentNode.removeChild(el); } if(fullW && fullH){ newW = Math.min(fullW, scrW); newH = newW * fullH / fullW; if(hS.viewImgCenter.val && newH > scrH){ newH = scrH; newW = newH * fullW / fullH; } if(newW/fullW < 1.13 && newW / fullW >0.88){ newW = +fullW; newH = +fullH; var title = x2 +'00%'; } } if(!title) title = Math.round(newW / fullW *100)* x2 +'%'; var isViewUrl = !hS.addImgs.val && a.getAttribute('data-viewUrl') ,url = isA ? a.href : isViewUrl || (a.previousSibling && a.previousSibling.firstChild.src)|| a.src; if(/#\.jpg/.test(url) ) url = a.firstChild.src; if(isViewUrl) title ='100%'; var ht ='