Участник:X-romix/compare2texts.js

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску
//Скрипт для поиска совпадающих подстрок в двух текстах

//Этот код выполняется в начале.
if (mw.config.get('wgAction') == 'edit' || mw.config.get('wgAction') == 'submit'){
  addOnloadHook(XRomix_Compare2texts_OnLoad)
}

/////////////////////////////////////////////////////////////////////////////
function XRomix_Compare2texts_OnLoad(){
	//Этот код выполнится после загрузки страницы
	var toolbar = document.getElementById('toolbar')
	var textbox = document.getElementById('wpTextbox1')
	if (!textbox || !toolbar) return
	addToolbarButton("Сравнение", XRomix_Compare2texts, 'btnXRomix_Compare2texts', 'Сравнение двух текстов', "");
	
	//Adds a text button to edit toolbar
	function addToolbarButton(name, onclick, id, tooltip, accesskey){
		var toolbar = document.getElementById('toolbar');
		if (!toolbar) return;
		var newBtn = document.createElement('input');
		newBtn.type = 'button'; 
		newBtn.style.background = '#adbede';
		newBtn.style.height = '22px'; 
		newBtn.style.verticalAlign = 'middle';
		if (name) newBtn.value = name; 
		if (onclick) newBtn.onclick = onclick;
		if (id) newBtn.id = id;
		if (tooltip) newBtn.title = tooltip; 
		if (accesskey) newBtn.accessKey = accesskey; 
		toolbar.appendChild(newBtn);
		return newBtn;
	}
	
}
 
/////////////////////////////////////////////////////////////////////////////
function insertAfter(parent, node, referenceNode) {
  parent.insertBefore(node, referenceNode.nextSibling);
}
 
/////////////////////////////////////////////////////////////////////////////
function XRomix_Compare2texts(){
 XRomix_CreateNewTextbox();
   
}

/////////////////////////////////////////////////////////////////////////////
function XRomix_CreateNewTextbox(){
 //var input = document.getElementById('wpSummary')
 var input = document.getElementById('wpTextbox1')
 if (!input) return
 var el = document.getElementById('XRomix_wpTextbox2')
 if (el) {
  removeElementById('XRomix_wpTextbox2');
  return;
 } 
 var el = document.createElement('span')
 el.id = 'XRomix_wpTextbox2';
 el.style.marginLeft = '3px'
 input.parentNode.insertBefore(el, input.nextSibling)
 
 el.innerHTML = '<p>Текст в окне ниже не будет сохранён на сервер.\
 <input type="button" value="Выполнить сравнение" onclick="XRomix_btnCompare2texts_Compare()">\
 <input type="button" value="Следующее соответствие" onclick="XRomix_btnCompare2texts_CompareNext()">\
 <br/><span id="XRomix_ComparedText"></span></p><textarea cols="80" rows="15" id="XRomix_Textbox2" name="XRomix_Textbox2"> \
 </textarea> \
 <input id="XRomix_h1" name="XRomix_h1" type="hidden" value="" /> \
 <input id="XRomix_h2" name="XRomix_h2" type="hidden" value="" /> ';
 
  //Вспомогательные функции
    function removeChildrenRecursively(node)  
    {  
        if (!node) return;  
        while (node.hasChildNodes()) {  
            removeChildrenRecursively(node.firstChild);  
            node.removeChild(node.firstChild);  
        }  
    }  
      
   function removeElementById(nodeId) {  
       document.getElementById(nodeId).parentNode.removeChild(  
                               document.getElementById(nodeId));  
   }   
  
}



/////////////////////////////////////////////////////////////////////////////
function XRomix_Compare2texts_Compare(mode){
	//////////////////////////////////////
	// Переменные для функции
	var XRomix_maxP1; //позиция максимально совпадающих фрагментов
	var XRomix_maxP2;
	var XRomix_max1;//длина максимвльно совпадающих фрагментов
	var XRomix_max2;
	var XRomix_len1;//длина текущего соответствия для checkCompareLength
	var XRomix_len2;
	var XRomix_s1// тексты для сравнения. в них знаки препинания и прочие различия удалены, но число и позиция символов сохранена
	var XRomix_s2

	var wpTextbox1=document.getElementById('wpTextbox1');
	if(!wpTextbox1) return;
	
	if(mode=="first"){
		var s= wpTextbox1.value;
		XRomix_s1=prepareText(s, true); //см. ниже
	}else if (mode=="next"){	
	    var h1=document.getElementById('XRomix_h1');
		XRomix_s1 = h1.value;
	}
	
	var wpTextbox2=document.getElementById('XRomix_Textbox2');
	if(!wpTextbox2) return;
	
	if(mode=="first"){
		var s= wpTextbox2.value;
		XRomix_s2=prepareText(s, false); //см. ниже
	}else if (mode=="next"){	
	    var h2=document.getElementById('XRomix_h2');
		XRomix_s2 = h2.value;
	}	
	
	getMaxSameText(); //см.ниже
	
	//alert("Совпадение1: "+wpTextbox1.value.substr(XRomix_maxP1, XRomix_max1))
	//alert("Совпадение2: "+wpTextbox2.value.substr(XRomix_maxP2, XRomix_max2))

	var sovp1=wpTextbox1.value.substr(XRomix_maxP1, XRomix_max1);
	var sovp2=wpTextbox2.value.substr(XRomix_maxP2, XRomix_max2);
	if(XRomix_max2>3){
		selectInTextArea(wpTextbox1, sovp1);
		selectInTextArea(wpTextbox2, sovp2);
		setSelectionRange(wpTextbox1, XRomix_maxP1, XRomix_maxP1+XRomix_max1);
		setSelectionRange(wpTextbox2, XRomix_maxP2, XRomix_maxP2+XRomix_max2);
		
		wpTextbox1.focus();
	
		
		var label = document.getElementById('XRomix_ComparedText');
		label.innerHTML="<i><font color='blue'>"+sovp2+"</font></i>";
		
		
		
	}else{
		var label = document.getElementById('XRomix_ComparedText');
		label.innerHTML="<font color='blue'>Совпадений не найдено</font>";
		//alert("Совпадений не найдено");
	}
	
	
	var s=XRomix_s1;
	var left=s.substr(0, XRomix_maxP1);
	var right=s.substr(XRomix_maxP1+XRomix_max1);
	var sp=generateSpaces(XRomix_max1);
	var XRomix_s1=left+sp+right;
	
	if(s.length!=XRomix_s1.length){
	  alert("Длина строк не совпадает");
	}
	
	var h1=document.getElementById('XRomix_h1');
	h1.value=XRomix_s1;
	var h2=document.getElementById('XRomix_h2');
	h2.value=XRomix_s2;
	
	//////////////////////////////////////
	//Выполняет замену подстроки s1 на s2 в строке s. Сама строка s при этом не изменяется
	function replace(s, s1, s2){
		var p=s.indexOf(s1);
		if (p<0) return;
		var left=s.substr(0, p);
		var right=s.substr(p+s1.length);
		return left+s2+right;
	}
	//////////////////////////////////////
	//генерирует строку из пробелов указанной длины
	function generateSpaces(len){
	    var s1="";
		for(var i=0; i<len; i++){
			s1+=" ";
		}
		return s1;
	}
  
	//////////////////////////////////////
	//Извлекает текстовое значение внутри [[викификации]], заменяя все лишнее на пробелы
	function spaceWikify(s){
		if (s.indexOf("|")>=0){
		//если найдена конструкция [[ |
			var flagSpace=1; //признак замены на пробел
			var s1="";
			for(var i=0; i<s.length; i++){
				var c=s.charAt(i);
				if(c=="|"){
					flagSpace=0;
					c=" ";
				}
				if(flagSpace==1){
					c=" ";
				}
				s1+=c;
			}
			s=s1;
		}
		s=s.replace(/\[\[/, "  "); //[[ на два пробела
		s=s.replace(/\]\]/, ""); //[[ на пустую строку, т.к. надо отработать еще слова наподобие  [[год]]у
		s=s+"  ";//добавляем два пробела в конец, чтобы скомпенсировать длину
		return s;
	}  

	//////////////////////////////////////
	//Подготавливает текст
	function prepareText(s, delQuoutes){
	 
		//Заменяем знаки препинания на пробелы
		s=s.replace(/[\.\,\;\?\!\(\)\—\-\:\=\*]/g, " ");
		
		//Заменим викификацию
		var arr=s.match(/\[\[.*?\]\]([^\s])*/g);
		if(arr){
			for(var i=0; i<arr.length; i++){
				var w=arr[i];
				var w1=spaceWikify(w); //см. ниже
				if(w.length!=w1.length) alert('Строки не совпадают по длине: '+w+'<>'+w1);
				s=replace(s, w, w1);
			}
		}

		//Убираем закавыченное, если передан такой параметр
		if(delQuoutes){
			var arr=s.match(/«.*?»/g);
			if(arr){
				for(var i=0; i<arr.length; i++){
					var w=arr[i];
					var w1=generateSpaces(w.length); //см. ниже
					s=replace(s, w, w1);
				}
			}
		}

		//Убираем то что внутри [квадратных скобок]
		var arr=s.match(/\[.*?\]/g);
		if(arr){
			for(var i=0; i<arr.length; i++){
				var w=arr[i];
				var w1=generateSpaces(w.length); //см. ниже
				s=replace(s, w, w1);
			}
		}
		
		
		//Убираем символы кавычек и апострофов
		s=s.replace(/[«»„“\"\']/g, " ");
	  
		//Убираем {{шаблоны}} Википедии
		var arr=s.match(/\{\{.*?\}\}/g);
		if(arr){
			for(var i=0; i<arr.length; i++){
				var w=arr[i];
				var w1=generateSpaces(w.length); //см. ниже
				s=replace(s, w, w1);
			}
		}

		//Переводим строку в нижний регистр
		s=s.toLocaleLowerCase();
		
		//Заменяем ё для целей сравнения
		s=s.replace(/ё/g, "е");
		
		//Заменяем различные пробельные символы на пробел
		s=s.replace(/\s/g, " ");
		
		return s 
	}


	//////////////////////////////////////
	//Измеряет длину совпадения в символах для строк  XRomix_s1 и XRomix_s2 начиная с указанных позиций p1 и p2
	function checkCompareLength(p1, p2){
	   var c1=XRomix_s1.charAt(p1);
	   var c2=XRomix_s2.charAt(p2);
	   if(c1!=c2){
			XRomix_len1=0; //
			XRomix_len2=0;
			return false;
	   }

	
	 var len1=XRomix_s1.length;
	 var len2=XRomix_s2.length;
	 var p1_=p1; //сохраняем позиции для вычисления длины
	 var p2_=p2;
	 var p1_nosp=-1; //позиция последнего совпадающего непробельного символа
	 var p2_nosp=-1;
	 
	 for(;;){
	   if(p1>=len1) break;
	   if(p2>=len2) break;
	   
	   c1=XRomix_s1.charAt(p1);
	   c2=XRomix_s2.charAt(p2);
	   if((c1==" ") && (c2==" ")){
		 //отыскиваем первый непробельный символ для p1
	     while(c1==" "){
		   p1++;
		   c1=XRomix_s1.charAt(p1);
		   if(p1>=len1) break;
		 }
		 //отыскиваем первый непробельный символ для p2
	     while(c2==" "){
		   p2++;
		   c2=XRomix_s2.charAt(p2);
		   if(p2>=len2) break;
		 }
	   }

	   //Пропускаем мягкий перенос с кодом 173 (дес) или 0xAD  
	  if(c1=="\xAD"){
	    p1++;
		continue;
	  }
	  if(c2=="\xAD"){
	    p2++;
		continue;
	  }
	  //Пропускаем символ ударения
	  if(c1=="́"){
	    p1++;
		continue;
	  }
	  if(c2=="́"){
	    p2++;
		continue;
	  }
	  
	  
	  
	  
	  if(c1==c2){
		if(c1!=" ") p1_nosp=p1;
		if(c2!=" ") p2_nosp=p2;
		p1++;
		p2++;
	 	continue;
	  }
	  break;
	 
	 }
		if(p1_nosp==-1){ //не совпал ни один символ
			XRomix_len1=0; //
			XRomix_len2=0;
		}else{
			XRomix_len1=p1_nosp-p1_+1; //
			XRomix_len2=p2_nosp-p2_+1;
		}
	  return true;
	}

	////////////////////////////////////////////////////////////////////////////
	//Отыскивает максимально совпадающий текст между XRomix_s1 и XRomix_s2
	//Помещет его начальные указатели в XRomix_maxP1 и XRomix_maxP1, и длины в XRomix_max1 и XRomix_max1
	function getMaxSameText(){
	  
	  XRomix_maxP1=0; 
	  XRomix_maxP2=0;
	  XRomix_max1=0;
	  XRomix_max2=0;
	  
	  var arr1 = new Array(); //массивы позиций начала слова для ускорения поиска
	  var arr2 = new Array();
	  
	  
	  //Заполняем массив позициями начала слова
	  var c=" ";
	  var wasSp=1; //признак наличия пробела до текущего символа
	  for(var i=0; i<XRomix_s1.length; i++){
	    c=XRomix_s1.charAt(i);
		if(c<=" "){
		  wasSp=1;
		  continue;
		}else{
		  if(wasSp==0) continue; //пропускаем не первые буквы каждого слова
		  wasSp=0;
		  arr1.push(i);
	    }	
	  }
	  //Для второй строки делаем то же самое
  	  var wasSp=1; //признак наличия пробела до текущего символа
	  for(var i=0; i<XRomix_s2.length; i++){
	    c=XRomix_s2.charAt(i);
		if(c<=" "){
		  wasSp=1;
		  continue;
		}else{
		  if(wasSp==0) continue; //пропускаем не первые буквы каждого слова
		  wasSp=0;
		  arr2.push(i);
	    }	
	  }
	  
	  for(var i=0; i<arr1.length; i++){
	    var p1=arr1[i];
	  
	    for(var j=0; j<arr2.length; j++){
			var p2=arr2[j];
			if(checkCompareLength(p1, p2)){
				if (XRomix_len2>XRomix_max2){ //сравниваем по второму тексту, т.к. там нет викификации
				  
				  XRomix_max1=XRomix_len1;
				  XRomix_max2=XRomix_len2;
				  XRomix_maxP1=p1;
				  XRomix_maxP2=p2;
				}
			}
		}
	    
	  }
	}
	



/////////////////////////////////////////////////////////////////////////////

	function selectInTextArea(txtarea, text1){
		
		//Обрезаем многострочный текст до одной строки - иначе поиск не срабатывает
		var arr=text1.split(/[\n\r]/);
		var max="";
		//найдем максимальную по длине строку из многострочного фрагмента
		for(var i=0; i<arr.length; i++){
		  if (max.length<arr[i].length){ max=arr[i]}
		}
		text=max;
		
		
		if (txtarea.setSelectionRange) {//Mozilla/Opera
			
			var selPos = txtarea.value.indexOf(text)
			if (selPos < 0) return
			txtarea.focus()
			txtarea.setSelectionRange(0, 0);//перейдем в начало

			//http://www.dynamicdrive.com/dynamicindex11/findpage.htm
			
			var  caseSensitive = true // is search case sensitive?
			var  backwards = false //should we also search backwards?
			var  wrapAround = true // should we wrap the search?
			try{
				self.find(text, caseSensitive, backwards, wrapAround);	
			}catch(e){
			}
			
		}else if (txtarea.createTextRange){ //IE
			var oRange = txtarea.createTextRange()
			if (oRange.findText(text))  oRange.select() 
		}
	}


	//подсчитывает концы строк в фрагменте текста
	function countCrlf(str){
	  var cnt=0;
	  for(var i=0; i<str.length; i++){
	    var c=str.charCodeAt(i);
		if (c==13){
		  cnt++;
		}
	  }
	  return cnt;
	}
	 
	//Браузеро-независимый setSelectionRange - изменяет начало и конец
	//выделенного фрагмента в поле ввода input
	function setSelectionRange(input, start, end) {
	  if (typeof wpTextbox1.selectionStart != 'undefined' 
	    && (navigator.productSub > 20031000 || is_safari)) { 
		//Mozilla/Opera/Safari3
		input.setSelectionRange(start, end);
	  }else if (document.selection && document.selection.createRange) { 
	 	 //Internet Explorer
			var range = input.createTextRange();
			range.collapse(true);
			range.moveStart("character", start - countCrlf(input.value.substring(0, start)));
			range.moveEnd("character", end - start - countCrlf(input.value.substring(start, end)));
			range.select();
	  }	 
	};


}//function


/////////////////////////////////////////////////////////////////////////////
function XRomix_btnCompare2texts_Compare(){
	XRomix_Compare2texts_Compare("first");
	var wpSummary = document.getElementById('wpSummary')
	if(wpSummary){
		var temp=wpSummary.value;
		temp=temp.replace(/\/\*.*?\*\// , ""); //комментарии
		temp=temp.replace(/[\s]*/ , ""); //пробелы
		if (temp==""){
			var s=wpSummary.value;
			s+=" [[User talk:X-romix/compare2texts.js|compare2texts.js]] - поиск совпадений";
			if (s.length<200) wpSummary.value=s;
		}
	}
} 



////////////////////////////////////////////////////////////////////////////
function XRomix_btnCompare2texts_CompareNext(){
  XRomix_Compare2texts_Compare("next");
}