Участник:Arbnos/wikilinker.js

Материал из Викицитатника
// Russian Wiki Linker
// Find link for selected text

if (/edit|submit/.test(wgAction)){
	var bpar = [WikiLinker, 'commons/b/bd/Wikilinker.png', 'Вики-ссыльщик. Подбирает вики-ссылку для выделенного слова или словосочетания'];
	// Чтоб работало и до, и после MediaWiki:Editpage.js
   if(addFuncBtn)addFuncBtn('wikilinker',bpar[0],bpar[1],bpar[2]);else{mwCustomEditButtons['wikilinker'] = bpar};
  if(typeof Ignatus == 'object'){
	  Ignatus.wikilinker = Ignatus.wikilinker || {} 
  }else{
	  Ignatus = { 'wikilinker': {} }
  };
  // Подключение дизамбигуатора
  if( !Ignatus.wikilinker.silent && (
  	 !("dabfix" in Ignatus.wikilinker) || (Ignatus.wikilinker.dabfix  &&  Ignatus.wikilinker.dabfix!='no')
    ) && !Ignatus.wikilinker.nodabdetect
      && (!Ignatus.dabfixer || !Ignatus.dabfixer.dabfix) // скрипт ещё не загружен
  ){
	  mw.loader.load('//ru.wikipedia.org/w/index.php?title=user:Ignatus/dabfixer.js&action=raw&ctype=text/javascript')
  };
}

function WikiLinker() {
	
    var CantWork = 'Сначала нужно выделить слово или словосочетание';
    var range;
    var browserType = 0;

    var txt = '', nft;
    var wpTextbox1 = document.editform.wpTextbox1;

    var winScroll = document.documentElement.scrollTop;

    wpTextbox1.focus();

    if (typeof wpTextbox1.selectionStart != 'undefined') { // Modern browsers
        browserType = 1;

        var textScroll = wpTextbox1.scrollTop;
        var startPos = wpTextbox1.selectionStart;
        var endPos = wpTextbox1.selectionEnd;
        txt = wpTextbox1.value.substring(startPos, endPos);

        // отсекаем последние пробелы
        while (txt.slice(-1) == ' ') {
           txt = txt.slice(0,-1);
           endPos = endPos - 1;
        }
        nft = mw.Title.newFromText(txt);

        if (txt == '') {
            alert(CantWork);
        }
        else {
            processText();
        }
        wpTextbox1.selectionStart = startPos;
        wpTextbox1.selectionEnd = startPos + txt.length;
        wpTextbox1.scrollTop = textScroll;
    } else if (document.selection && document.selection.createRange) { //IE
        browserType = 2;

        range = document.selection.createRange();
        txt = range.text;

        // отсекаем последний пробел
        while (txt.slice(-1) == ' ') {
           txt = txt.slice(0,-1);
           range.moveEnd('character', -1);
        }
		nft = mw.Title.newFromText(txt);
        if (txt == '') {
            alert(CantWork);
        }
        else {
            processText();
        }
        
    } else { // other browsers
        alert("Your browser is not supported by Wikilinker :(");
    }

    document.documentElement.scrollTop = winScroll // scroll back, for IE/Opera

    function processText() {
        var phrase=txt.substr(0,100);

   		phrase=phrase.replace(/\s+|—|[_!?.,:]/g, " ");
   		phrase=phrase.replace(/»|«|\%|\'|\"/g, "");
		phrase=phrase.replace(/(^\s+)|(\s+$)/g, "");
        
        if( "exceptions" in Ignatus.wikilinker && initialLower(phrase) in Ignatus.wikilinker.exceptions ){
	        recordText( Ignatus.wikilinker.exceptions[initialLower(phrase)] );
	        return 0
	    };

        $.ajax({
        type: "GET", // request type ( GET or POST )
        url: mw.util.wikiScript('api') + '?action=query&list=search&srlimit=5&srprop=redirecttitle&format=json&srsearch=' + phrase,
        dataType: 'json',
        success: stateChanged
        })
    }
    
    function joino(o){
    	var a = new Array();
    	for(var e in o){a.push(e)};
    	return a.join('|')
    }
    
    function likeness(q){// Оценка сходства результата запроса с txt
    	var TXT = txt.toUpperCase(), i=0;
    	q = q.toUpperCase();
    	while( i<TXT.length  &&  i<q.length  && q.substr(0,i) == TXT.substr(0,i) )i++;
    	if( TXT.charAt(i+1)==q.charAt(i) || TXT.charAt(i)==q.charAt(i+1) )i+=0.5;// беглые гласные
    	return ( ( q.length-i && (q.length-i<4 ? (q.length-i)/6.01 : 0.499) )
    	       + ( TXT.length-i && (TXT.length-i<4 ? (TXT.length-i)/6.01 : 0.499) ) - i/TXT.length )
    	// Функция смещает максимум на 1 позицию вверх или чуть менее вниз
    }
    
    function warnred(isred,l){
    	if(Ignatus.wikilinker.warnredirects && isred){
 			warna('Добавлена ссылка на перенаправление на', l, 'подставить основное имя', function(){
 				$("#wpTextbox1").textSelection(
			     'encapsulateSelection',
			     { 'peri': "", 'selectPeri': true, 'replace': true }
			    );
			    var restoretxt = /\[\[(?:.*\|)?(.*)\]\](.*)?/.exec(txt);
			    if(restoretxt)txt = restoretxt[1]+(restoretxt[2]||'');
 				recordText( l )
 			})
 		}
    }
    
    function loadpageprops(arr) {
        var onreadystatechange = function(resp){
        	var p = resp.query.pages;
        	var id=false, rank=false;
        	// [КОСТЫЛЬ] на глюк Цирруса: не опознаёт страницы #перенаправление [[…]] как надо
			/*[*/if('redirects' in resp.query){
				for(var rn in resp.query.redirects){
					arr[resp.query.redirects[rn].to] = arr[resp.query.redirects[rn].from]
				}
			}/*] Впрочем, можно и оставить для чего-нибудь*/
        	for(var a in p){// Выбираем самый ранжированый результат из не-дизамбигов
            	if( ("missing" in p[a]) || ("invalid" in p[a]) ){
            		//нет страницы
        		}else{
        			// Расчёт "веса" варианта с учётом места в выдаче,
        			var this_rank = arr[p[a].title][0] + 
        			// дизамбиг или нет,
        			 ( "pageprops" in p[a] && "disambiguation" in p[a].pageprops
        			  ? ('penda' in Ignatus.wikilinker ? Ignatus.wikilinker.penda : 1000)
        			  : 0
        			 ) + 
        			// а также относительной схожести основного имени с исходным заданием
        			 ( arr[p[a].title]==txt ? -1 : likeness(p[a].title) );
        			 	
        			if(
            			!id || this_rank < rank
            		){
            			id = a;
            			rank = this_rank
            		}
        		}
            }


            if( "pageprops" in p[id] && "disambiguation" in p[id].pageprops ){//это дизамбиг
            	operate_on_disambig(p[id].title)
            };
            
            if(Ignatus.wikilinker.noredirects){
            	recordText( p[id].title )
     		}else{
     			warnred( arr[p[id].title][1] != p[id].title  &&  !(arr[p[id].title][0]==1 && nft && nft.getPrefixedText()==p[id].title), p[id].title );
     			recordText( (arr[p[id].title])[1] )
	     	}
	    };//onreadystatechange
	    
        $.ajax({
        type: "GET", // request type ( GET or POST )
        url: mw.util.wikiScript('api') + '?action=query&prop=pageprops&format=json&redirects=1&titles='
         + encodeURIComponent(joino(arr)),
        dataType: 'json',
        success: onreadystatechange
        })
    }
	
    function operate_on_disambig(t){
    	if(Ignatus.wikilinker.silent)return;// нечего делать
    	var df;
    	if( !("dabfix" in Ignatus.wikilinker)  ||  Ignatus.wikilinker.dabfix  &&  Ignatus.wikilinker.dabfix!='no' ){
    		df = Ignatus.wikilinker.dabfix || Ignatus.dabfixer.dabfix
    	};
    	warna('Добавлена ссылка на неоднозначность',t,(df?'разрешить':null),function(){df(t)})
    }
	
	function warna(c,t,l,f){
	    if(Ignatus.wikilinker.silent)return;
	    var popup = document.createElement('div');
	    // Текст сообщения
	    popup.appendChild(document.createTextNode(c+' '));
	    popup.style='background:white;';
	    var clorrer = function(){popup.parentElement.removeChild(popup);return false};
	    // Ссылка на статью t
	    var a = document.createElement('a');
	    a.href = wgServer+mw.util.getUrl(t);
	    a.appendChild(document.createTextNode(t));
	    popup.appendChild(a);
	    // Предложение действия
	    if(l){
	    	popup.appendChild(document.createTextNode(', '));
	    	var b = document.createElement('a');
	    	b.appendChild(document.createTextNode(l));
	    	b.onclick = function(){clorrer();if(f)f();}
	    	popup.appendChild(b);
	    }
	    // Крестик-убиралка
	    a = document.createElement('span');
	    a.appendChild(document.createTextNode('\u00d7'));
	    a.style.color="gray";a.style.fontWeight="bold";a.style.marginLeft="1em";
	    a.onclick = clorrer;
	    popup.appendChild(a);
	    // Добавляем сообщение на верх формы редактирования
	    var ef = document.getElementById("editform");
	    ef.insertBefore(popup,ef.firstChild)
	}
    
    function initialLower(str) {
       return str.substr(0, 1).toLowerCase() + str.substr(1);
    }
    
    function recordText(page_name){
	    // для "форумы" будет "[[форум]]ы", а не "[[форум|форумы]]"
	   if ( page_name != '' && initialLower(txt.substr(0, page_name.length)) == initialLower(page_name)  &&  page_name.length <= txt.length) {
          txt = '[[' + txt.substr(0, page_name.length) + ']]' + txt.substr(page_name.length, txt.length - page_name.length) ;
       }
       else {
          txt = '[[' + page_name + '|' + txt + ']]'; 
       }

       if (browserType == 1) { //Mozilla/Opera/Safari3
           wpTextbox1.value = wpTextbox1.value.substring(0, startPos) + txt + wpTextbox1.value.substring(endPos);

           wpTextbox1.selectionStart = startPos;
           wpTextbox1.selectionEnd = startPos + txt.length;
           wpTextbox1.scrollTop = textScroll;
       } else if (browserType == 2) { //IE
            range.text = txt;

       } else { // other browsers
           alert(CantWork);
       }
    }

    function stateChanged(resp) {
        var pn = new Object(), i = 0;
        
        while (typeof resp.query.search[i++] != 'undefined') {
           var next_elem = [
           	i,// ВНИМАНИЕ! на следующей строчке — [КОСТЫЛЬ], который может понадобиться выкинуть
           	resp.query.search[i-1].redirecttitle /*[*/ && resp.query.search[i-1].redirecttitle.mTextform /*]*/
           	|| resp.query.search[i-1].title // редирект ближе к требуемому
           ];
           // Если текст первого варианта равен в смысле вики-ссылки txt, меняем его на txt
           if( i==1  &&  nft && nft.getPrefixedText() == next_elem[1] ) next_elem[1]=txt;
           pn[resp.query.search[i-1].title] = next_elem
        }
        // Проверяем, дизамбиг ли и есть ли по исходному имени
        if( i>1 ){//что-то нашли 
               	if( Ignatus.wikilinker.silent || Ignatus.wikilinker.nodabdetect ){//нас не интересую дизамбиги
               		var sn = 0;
               		if(
               			likeness(resp.query.search[0].title) - likeness(resp.query.search[1].title) >= 1 
               			&& ( !nft || pn[resp.query.search[0].title][1] != nft.getPrefixedText() )
               		){
               			sn=1;//перестановка вариантов, если второй куда ближе к исходнику
               			warnred( resp.query.search[1].redirecttitle, resp.query.search[1].title );// NB! Не закостылено по глюку CirrusSearch, см. выше
               		}else{warnred( resp.query.search[0].redirecttitle, resp.query.search[0].title )};// Поскольку основное назначение режима — уменьшать кол-во запросов, то и не будем костылить
               		
               		for(var z in pn){ if(pn[z][0]==sn ){
               			recordText( Ignatus.wikilinker.noredirects ? z : pn[z][1] ) 
               		}}
               	}else{ loadpageprops(pn) }
        }else{//ничего не найдено
            var sugg = resp.query.searchinfo.suggestion;
            warna("Ничего не нашлось по запросу",txt,"подставить «"+sugg+"»",function(){
            	$("#wpTextbox1").textSelection(
			     'encapsulateSelection',
			     { 'peri': sugg, 'selectPeri': true, 'replace': true }
			    );
            });
	        recordText("")
	    }
    }
}