User:Khanson/check tags.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
Documentation for this user script can be added at User:Khanson/check tags. |
//Скрипт проверяет правильность закрытия тегов.
//Автор: X-romix
if (wgAction == 'edit' || wgAction == 'submit')
$(function(){
var wpSave = document.getElementById('wpSave');
if (!wpSave) return;
addHandler(wpSave, 'click', XRomix_CheckTagsHandler);
});
function XRomix_CheckTagsHandler(e){
var xSelectionStart=0;
var xSelectionEnd=0;
var wpTextbox1 = document.editform.wpTextbox1;
var isCancel=false;
var text=document.editform.wpTextbox1.value;
var isCancel=!XRomix_CheckTags(text);
if(isCancel){
setSelectionRange(wpTextbox1, xSelectionStart, xSelectionEnd);
isCancel= !window.confirm("Имеются ошибки форматирования. Всё равно сохранить?");
}else{
setRedWindow(""); //Очищаем красное окно, если оно было
}
if (isCancel){ //отменить нажатие кнопки
e = e || window.event //из-за различий в IE и стандартных браузерах
if (e.preventDefault) e.preventDefault(); else e.returnValue = false //остановить действие, снова по-разному
return false //на всякий случай
}
//Выводит красное окно предупреждения
function setRedWindow(res){
var w = document.getElementById('XRomix_editpage_CheckTags');
if (res!=""){
var wpSummary = document.getElementById('wpSummary')
if (!wpSummary) return
var w = document.getElementById('XRomix_editpage_CheckTags');
if(!w){
w = document.createElement('span')
w.id = 'XRomix_editpage_CheckTags'
wpSummary.parentNode.insertBefore(w, wpSummary.nextSibling);
}
w.innerHTML = '<div style="padding:10px; margin:5px; background:#FF8080; border:1px solid red;">'+
'Имеются незакрытые (или неправильно закрытые) элементы HTML или вики-разметки. ' + res +' '+
' (<a href="' + wgArticlePath.replace(/\$1/, 'Википедия:Как править статьи') +
'" title="(ссылка откроется в новом окне)" target=_blank>подробнее ↗</a>)</div>';
}else{
if(w) w.innerHTML=''; //Если нет ошибки, то очищаем
}
}
//////////////////////////////////////
//генерирует строку из пробелов указанной длины
function generateSpaces(len){
var s1=" ";
while(s1.length<len){
s1+=s1;
}
return s1.substr(0,len);
}
//////////////////////////////////////
//Проверяет теги
function XRomix_CheckTags(text){
//////////////////////////////////////
//Заменяет текст между открывающим и закрывающим тегами на пробелы
function replaceTags(tag1, tag2){
while(1==1){
var p=txt.search(tag1);
if(p==-1) break;
var p1=txt.indexOf(tag2,p);
if(p1==-1) { //ошибка, закрывающий тег не найден
setRedWindow('Не закрыт элемент '+htmlEncode(tag1)+'.');
xSelectionStart=p;
xSelectionEnd=p+tag2.length+1;
return false;
}
var w1=generateSpaces(p1-p+tag2.length);
var left=txt.substr(0, p);
var right=txt.substr(p1+tag2.length);
txt=left+w1+right;
}
return true;
}
//////////////////////////////////////
//Заменяет тег на пробелы
function replace1Tag(tag1){
var p=txt.indexOf(tag1);
if(p==-1) return;
var w1=generateSpaces(tag1.length);
var left=txt.substr(0, p);
var right=txt.substr(p+tag1.length);
txt=left+w1+right;
}
var ok=0;
if (document.URL.match(/\.js&action=(edit|submit)/)){
return true; //JavaScript не проверяем
}else if (document.URL.match(/\.css&action=(edit|submit)/)){
return true; //CSS не проверяем
}else if (wgTitle.match (/(Шаблон|Template)\:/)){
return true; //Шаблоны не проверяем
}else if ('code'.replace(/d/g, 'r') != 'core'){ //Проверяем, поддерживает ли браузер регулярные выражения (RegExp)
return true;
}
var wpTextbox1 = document.editform.wpTextbox1;
if(!wpTextbox1) return true;
var txt = wpTextbox1.value;
var ok=replaceTags(/<nowiki>/, "</nowiki>");
if(!ok) return false;
var ok=replaceTags(/<\!\-\-/, "-->");
if(!ok) return false;
var ok=replaceTags(/<math>/, "</math>");
if(!ok) return false;
var ok=replaceTags(/<source[\s>]/, "</source>");
if(!ok) return false;
var ok=replaceTags(/<pre[\s>]/, "</pre>");
if(!ok) return false;
var arrPos = new Array();
var arrTags= new Array();
var arr = txt.match(/(<[\/]?[A-Za-z][^>]*>|\[\[|\]\]|\{\{|\}\})/g);
if(arr==null) return true; //Нет тегов для проверки - возврат
if(arr){
for(var i=0; i<arr.length; i++){
var w=arr[i];
var pos=txt.indexOf(w);
//Запоминаем позицию и текст тега
arrPos.push(pos);
arrTags.push(w);
//Заменяем тег на пробелы
replace1Tag(w);
}
}
var arrStackTags = new Array();
var arrStackPos = new Array();
for(var i=0; i<arrTags.length; i++){
var TagName=arrTags[i];
var TagPos=arrPos[i];
if (TagName.search(/(\/[\s]*>|<hr>|<br>)/) >=0 ){ // <тег/>
continue; //пропускаем такие теги, поскольку они не требуют закрытия
}else if(TagName.search(/<[a-zA-Z][^>]*>/) >=0){
//открывающий <тег ...>
//помещаем тег и его позицию в стек
arrStackTags.push(TagName);
arrStackPos.push(TagPos);
}else if(TagName.search(/<\/[^>]+>/) >=0){
//Закрывающий </тег>
var TagNameN=TagName.replace(/[<>\/]/g, "");
TagNameN=TagNameN.toLowerCase(); //Нормализованное имя тега - без угловых скобок и /
if (arrStackTags.length==0){
setRedWindow('Неожиданный закрывающий тег '+htmlEncode(TagName)+'.')
xSelectionStart=TagPos;
xSelectionEnd=TagPos+TagName.length;
return false;
}else{
var TagName2=arrStackTags.pop();
var TagPos2=arrStackPos.pop();
var TagName2N=TagName2.replace(/[\s][^>]*/, ""); //убираем все после первого пробела
TagName2N=TagName2N.replace(/[<>\/]/g, "");//убираем </>
TagName2N=TagName2N.toLowerCase();
if(TagNameN!=TagName2N){
setRedWindow('Неожиданный закрывающий тег '+htmlEncode(TagName)+' после открывающего тега '+htmlEncode(TagName2)+'.');
xSelectionStart=TagPos;
xSelectionEnd=TagPos+TagName.length;
return false;
}
}
}else if(TagName=="[["){
arrStackTags.push(TagName);
arrStackPos.push(TagPos);
}else if(TagName=="{{"){
arrStackTags.push(TagName);
arrStackPos.push(TagPos);
}else if(TagName=="]]"){
if (arrStackTags.length==0){
setRedWindow('Неожиданный закрывающий элемент '+htmlEncode(TagName)+'.');
xSelectionStart=TagPos;
xSelectionEnd=TagPos+TagName.length;
return false;
}else{
var TagName2=arrStackTags.pop();
var TagPos2=arrStackPos.pop();
if(TagName2!="[["){
setRedWindow('Неожиданный закрывающий тег '+htmlEncode(TagName)+' после открывающего тега '+htmlEncode(TagName2)+'.');
xSelectionStart=TagPos;
xSelectionEnd=TagPos+TagName.length;
return false;
}
}
}else if(TagName=="}}"){
if (arrStackTags.length==0){
setRedWindow('Неожиданный закрывающий элемент '+htmlEncode(TagName)+'.');
xSelectionStart=TagPos;
xSelectionEnd=TagPos+TagName.length;
return false;
}else{
var TagName2=arrStackTags.pop();
var TagPos2=arrStackPos.pop();
if(TagName2!="{{"){
setRedWindow('Неожиданный закрывающий тег '+htmlEncode(TagName)+' после открывающего тега '+htmlEncode(TagName2)+'.');
xSelectionStart=TagPos;
xSelectionEnd=TagPos+TagName.length;
return false;
}
}
}
}//for
if(arrStackTags.length>0){
var TagName=arrStackTags.pop()
var TagPos=arrStackPos.pop();
setRedWindow('Имеется незакрытый элемент '+htmlEncode(TagName)+'.');
xSelectionStart=TagPos;
xSelectionEnd=TagPos+TagName.length;
return false;
}//if
return true; //нет ошибок
}//function
//подсчитывает концы строк в фрагменте текста
function countCrlf(str){
str=""+str;
var arr=str.match(/\n/g);
//Теперь массив содержит концы строк - возвращаем его длину
if (arr) return arr.length;
return 0;
}
//Извлекает первую строку (до \n или \r) из строки
function get1string(text){
var p=text.indexOf("\n");
var p1=text.indexOf("\r");
if(p1!=-1 && p!=-1){
if(p>p1) p=p1;
}
if(p!=-1){
text=text.substr(0, p);
}
return text;
}
//Браузеро-независимый setSelectionRange - изменяет начало и конец
//выделенного фрагмента в поле ввода input
function setSelectionRange(input, start, end){
if(!input) return;
if(!input.value) return;
var text=input.value.substring(start, end);
text=get1string(text);
if (input.setSelectionRange) {//Mozilla/Opera
//Попытаемся спозиционировать курсор на нужный текст
if (self.find) {//Mozilla, в Опере не работает
input.focus();
input.setSelectionRange(start, start);
var caseSensitive = false
var backwards = false
var wrapAround = false
self.find(text, caseSensitive, backwards, wrapAround);
}
//Выделим диапазон
input.focus();
input.setSelectionRange(start, end);
}else if (document.selection && document.selection.createRange) { //Internet Explorer
//IE проглючивает с началом и концом выделенного фрагмента - вносим исправление
var cnt1=countCrlf(input.value.substring(0, start));
var cnt2=countCrlf(input.value.substring(start, end));
var range = input.createTextRange();
if(range.findText){
input.focus();
range.collapse(true);
range.moveStart("character", start - cnt1);
range.moveEnd("character", start - cnt1);
range.select();
range.findText(text);
range.collapse(true);
}
var range = input.createTextRange();
//Выделяем диапазон в IE
input.focus();
range.collapse(true);
range.moveStart("character", start - cnt1);
range.moveEnd("character", end - start - cnt2);
range.select();
}
}
//Кодирование спецсимволов HTML
function htmlEncode(s){
s=s.replace(/[\&]/g, "&");
s=s.replace(/[<]/g, "<");
s=s.replace(/[>]/g, ">");
return s;
}
}