User:Khanson/compare2texts.js
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// <nowiki>
//Скрипт для поиска совпадающих подстрок в двух текстах
//Этот код выполняется в начале.
if (wgAction == 'edit' || 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:Khanson/compare2texts.js|compare2texts.js]] - поиск совпадений";
if (s.length<200) wpSummary.value=s;
}
}
}
////////////////////////////////////////////////////////////////////////////
function XRomix_btnCompare2texts_CompareNext(){
XRomix_Compare2texts_Compare("next");
}
// </nowiki>