Code: Select all
///Validates selected XML fragment or whole document, pointing potential error place
// http://akelpad.sourceforge.net/forum/viewtopic.php?p=15256#p15256
// https://github.com/FeyFre/akelpad-customisations/blob/scripts/XMLValidator.js
// Version: ???
// Version: 2.0 (2016.02.28) by VladSh
// Version: 1.9 (2016.02.24) by Skif_off
// Version: 1.8 (2015.04.10) by VladSh
// Version: 1.7 (2014.12.01) by VladSh
// Version: 1.6 (2012.10.24) by VladSh
// Version: 1.0 (2011.10.25) (c) Panych Y.W. aka FeyFre
//
// Arguments:
// validate:
// • [true | без параметра] - производить проверку синтаксиса xml и валидацию согласно схеме;
// • false - только проверку правильности структуры xml
// parse2gt:
// • [false | без параметра] - не производить проверку удвоенного символа '>'
// • true - производить
// keep:
// • [0 | без параметра] - не сохранять исходное выделение, устанавливая курсор в место ошибки
// • 1 - сохранять исходное выделение; если выделения нет, то курсор также будет установлен в место ошибки
// msgOpts - параметры вывода сообщений:
// • [0 | без параметра] - в диалоговое окно;
// • перечень кодов сообщений через запятую, которые выводить в панели вывода Log-плагина:
// 1 - все сообщения/ошибки;
// 16 - критическая ошибка xml-парсера;
// 48 - ошибка xml-синтаксиса (можно выводить только её, т.к. можно переходить на место ошибки);
// 64 - сообщение об успешно пройденной валидации
// append - параметры вывода в панель Log-плагина:
// • [0 | без параметра] - очищать содержимое панели перед записью
// • 1, 2 - не очищать (см. Log-Rus.txt -> вызов с кодом 4 -> параметр "APPEND")
// lClose - параметры закрытия панели Log-плагина (при msgOpts=1 игнорируется):
// • [0 | без параметра] - не закрывать (в панели может остаться нужный текст, в т.ч. и от других вкладок программы);
// • 1 - закрывать только при успешно пройденной валидации;
// • 2 - закрывать всегда (только при msgOpts=0)
//
// Examples:
// Call("Scripts::Main", 1, "XMLValidator.js") - вывод в обычное диалоговое сообщение
// Call("Scripts::Main", 1, "XMLValidator.js", `-msgOpts=1`) - вывод всех сообщений в панель Log-плагина
// Call("Scripts::Main", 1, "XMLValidator.js", `-msgOpts=48 -lClose=1`) - вывод в панель Log-плагина только сообщений об ошибках xml-синтаксиса с закрытием панели при успешно пройденной валидации
// Call("Scripts::Main", 1, "XMLValidator.js", `-msgOpts=48 -append=1`) - вывод в панель Log-плагина только сообщений об ошибках xml-синтаксиса с добавлением их к уже существующим в панели сообщениям
var hWndEdit = AkelPad.GetEditWnd();
var parserName = "msxml2.DOMDocument";
var xml = new ActiveXObject(parserName);
try {
xml.async = false;
xml.validateOnParse = AkelPad.GetArgValue("validate", true);
xml.resolveExternals = false;
} catch (e) {
output("Internal parser " + parserName + " error: " + e.description, 16 /*MB_ICONSTOP*/);
}
var text = AkelPad.GetSelText();
var selection;
var bKeep = false;
if (!text) {
text = AkelPad.GetTextRange(0, -1);
selection = false;
} else {
selection = true;
var npStart = AkelPad.GetSelStart();
var npEnd = AkelPad.GetSelEnd();
bKeep = AkelPad.GetArgValue("keep", 0);
var npMin = Math.min(AkelPad.GetSelStart(), AkelPad.GetSelEnd());
}
var filepos, linepos, oLine;
var linenum = 0;
var t = new Date();
var extInfo = t.toLocaleTimeString() + " " + (AkelPad.GetEditFile(0) || "*") + " -> ";
var aFind = [];
xml.loadXML(text);
if (xml.parseError.errorCode !== 0) {
var err = xml.parseError;
linepos = err.linepos;
if (selection) {
filepos = npMin + err.filepos;
oLine = getLogLine(filepos);
linenum = oLine.linenum;
}
else {
linenum = err.line;
filepos = err.filepos;
}
if (!bKeep)
AkelPad.SetSel(filepos, filepos);
output(extInfo + "XML validation error:\r(" + linenum + "," + linepos + ")\t\t" + err.reason, 48 /*MB_ICONEXCLAMATION*/);
} else {
if (AkelPad.GetArgValue("parse2gt", false)) {
if (FindText(0, "<([^>\\s]++)\\s*\\K>>++(?![^<]*+</\\1)", selection ? 0x00480001 : 0x00280001 /*FRF_SELECTION|FRF_BEGINNING|FRF_REGEXP|FRF_DOWN*/, aFind)) {
filepos = aFind[0];
} else {
if (aFind[0])
WScript.Echo("Error in regular expression at position: " + (aFind[0] - 1));
// else
// WScript.Echo("Not found!");
}
if (filepos >= 0) {
filepos += 1;
// if (selection)
// filepos = npMin + filepos;
oLine = getLogLine(filepos);
linenum = oLine.linenum;
linepos = filepos - oLine.startpos;
if (!bKeep)
AkelPad.SetSel(filepos, filepos);
output(extInfo + "XML parsing error:\r(" + linenum + "," + linepos + ")\t\t'>>' was found.", 48 /*MB_ICONEXCLAMATION*/);
}
}
output(extInfo + "XML fragment is valid.\r", 64 /*MB_ICONINFORMATION*/);
}
function output(msg, nIcon /*MB_ICON...*/) {
var slog = AkelPad.GetArgValue("msgOpts", "0");
if (slog == "1" || (slog != "0" && slog.indexOf(nIcon.toString()) != -1)) {
var fLogOutput = "Log::Output";
if (AkelPad.Call(fLogOutput, 1, "", "", "^\\((\\d+),(\\d+)\\)", "/GOTOLINE=\\1:\\2") != -1) {
var nAppend = AkelPad.GetArgValue("append", 0);
AkelPad.Call(fLogOutput, 4, msg, -1, nAppend);
}
} else {
var lClose = AkelPad.GetArgValue("lClose", 0);
if ((lClose == 1 && nIcon == 64) || lClose == 2)
AkelPad.Call("Log::Output", 6);
AkelPad.MessageBox(hWndEdit, msg, WScript.ScriptName, nIcon);
}
WScript.Quit();
}
// Function returns a LineObject for Log-panel
function getLogLine(filepos) {
var wrpLine = AkelPad.SendMessage(hWndEdit, 1078 /*EM_EXLINEFROMCHAR*/, 0, filepos);
var start = AkelPad.SendMessage(hWndEdit, 187 /*EM_LINEINDEX*/, wrpLine, 0);
var unwrpLine = AkelPad.SendMessage(hWndEdit, 3143 /*AEM_GETUNWRAPLINE*/, wrpLine, 0);
return {
linenum: unwrpLine + 1,
startpos: start - 1
};
}
function FindText(hEditWnd, sFindIt, nFlags, aFind)
{
var nAEFR = 0x00200000 /*AEFR_REGEXPMINMATCH*/;
var lpText = AkelPad.MemAlloc((sFindIt.length + 1) * 2);
var lpFind = AkelPad.MemAlloc(_X64 ? 136 : 68 /*sizeof(AEFINDTEXTW)*/);
var nAEGI1;
var nAEGI2;
var bFound;
if (! hEditWnd)
hEditWnd = AkelPad.GetEditWnd();
aFind.length = 0;
if ((nFlags & 0x00000001 /*FRF_DOWN*/) || (nFlags & 0x00200000 /*FRF_BEGINNING*/) || (nFlags & 0x00400000 /*FRF_SELECTION*/))
nAEFR |= 0x00000001 /*AEFR_DOWN*/;
if (nFlags & 0x00000002 /*FRF_WHOLEWORD*/)
nAEFR |= 0x00000002 /*AEFR_WHOLEWORD*/;
if (nFlags & 0x00000004 /*FRF_MATCHCASE*/)
nAEFR |= 0x00000004 /*AEFR_MATCHCASE*/;
if (nFlags & 0x00040000 /*FRF_REGEXPNONEWLINEDOT*/)
nAEFR |= 0x00100000 /*AEFR_REGEXPNONEWLINEDOT*/;
if (nFlags & 0x00080000 /*FRF_REGEXP*/)
nAEFR |= 0x00080000 /*AEFR_REGEXP*/;
if (nFlags & 0x00200000 /*FRF_BEGINNING*/)
{
nAEGI1 = 1 /*AEGI_FIRSTCHAR*/;
nAEGI2 = 2 /*AEGI_LASTCHAR*/;
}
else if (nFlags & 0x00400000 /*FRF_SELECTION*/)
{
nAEGI1 = 3 /*AEGI_FIRSTSELCHAR*/;
nAEGI2 = 4 /*AEGI_LASTSELCHAR*/;
}
else if (nFlags & 0x00000001 /*FRF_DOWN*/)
{
nAEGI1 = 5 /*AEGI_CARETCHAR*/;
nAEGI2 = 2 /*AEGI_LASTCHAR*/;
}
else //(nFlags & FRF_UP)
{
nAEGI1 = 1 /*AEGI_FIRSTCHAR*/;
nAEGI2 = 5 /*AEGI_CARETCHAR*/;
}
AkelPad.MemCopy(lpText, sFindIt, 1 /*DT_UNICODE*/);
AkelPad.MemCopy(lpFind /*dwFlags*/, nAEFR, 3 /*DT_DWORD*/);
AkelPad.MemCopy(_PtrAdd(lpFind, _X64 ? 8 : 4) /**pText*/, lpText, 2 /*DT_QWORD*/);
AkelPad.MemCopy(_PtrAdd(lpFind, _X64 ? 16 : 8) /*dwTextLen*/, sFindIt.length, 2 /*DT_QWORD*/);
AkelPad.MemCopy(_PtrAdd(lpFind, _X64 ? 24 : 12) /*nNewLine*/, 3 /*AELB_ASIS*/, 3 /*DT_DWORD*/);
AkelPad.SendMessage(hEditWnd, 3130 /*AEM_GETINDEX*/, nAEGI1, _PtrAdd(lpFind, _X64 ? 32 : 16) /*crSearch.ciMin*/);
AkelPad.SendMessage(hEditWnd, 3130 /*AEM_GETINDEX*/, nAEGI2, _PtrAdd(lpFind, _X64 ? 56 : 28) /*crSearch.ciMax*/);
if (bFound = AkelPad.SendMessage(hEditWnd, 3041 /*AEM_FINDTEXTW*/, 0, lpFind))
{
aFind[0] = AkelPad.SendMessage(hEditWnd, 3136 /*AEM_INDEXTORICHOFFSET*/, 0, _PtrAdd(lpFind, _X64 ? 80 : 40) /*crFound.ciMin*/);
aFind[1] = AkelPad.SendMessage(hEditWnd, 3136 /*AEM_INDEXTORICHOFFSET*/, 0, _PtrAdd(lpFind, _X64 ? 104 : 52) /*crFound.ciMax*/);
}
else
aFind[0] = AkelPad.MemRead(_PtrAdd(lpFind, _X64 ? 128 : 64) /*nCompileErrorOffset*/, 2 /*DT_QWORD*/);
AkelPad.MemFree(lpText);
AkelPad.MemFree(lpFind);
return bFound;
}