Middawaida:YMS/eagleeye.js
Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.
- Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
- Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
- Internet Explorer/Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
- Opera: Strg+F5
/**
* EagleEye
* Tool for searching, spotting and correcting hard-to-sport errors
* For documentation see [[de:User:YMS/EagleEye]]
* <nowiki> ([[bugzilla:8761]])
*/
(function() {
// JavaScript strict mode
"use strict";
// Options and Ruleset
var ruleset = getOption("eeRuleset", mw.config.get("wgFormattedNamespaces")[2] + ":" + mw.config.get("wgUserName") + "/eagleeye.ruleset.js"); // The ruleset file (using a .js file in your user space prevents others from changing your rules)
var lang = getOption("eeLang", getDefaultLang(["en", "de"])); // Language (currently supported: de, en; default: user language or project language or en, whichever first is available)
var activated = getOption("eeActivate", [true, true, true]); // Activate scanner (portlet and tool itself) / marker / corrector (button and script itself)
var namespaces = getOption("eeNamespaces", [[0], [0], [0]]); // Namespaces for scanner (default value only) / marker / corrector
var useSkiplist = getOption("eeUseSkiplist", [true, false, false]); // Whether the skiplist should be used for scanner / marker / corrector ('''NOTE: not yet implemented for marker/corrector''')
var mPrefix = getOption("eeMarkerPrefix", "ee_"); // Prefix for markers (e.g. "ee_" will result in markers like "ee_Doppelwort", may be set to "" for no prefix)
var mStyle = getOption("eeMarkerStyle", "background-color: #FF9191;"); // CSS style for the marker highlighting (more can be defined for span.eeMarker in user CSS)
var sChunkSize = getOption("eeScannerChunkSize", 10000000); // The chunk size for the database scanner (too low values will fail [depends on database] or cause bad performance, too high values may cause bad performance or even crashes)
var sLimit = getOption("eeScannerLimit", 0); // The maximum number of results scanner should find (0 for no limit)
var sListAll = getOption("eeScannerListAll", false); // Whether the scanner should list all matches for a page instead of only the first one
var sListReplacements = getOption("eeScannerListReplacements", false); // Whether the scanner should list replacements, too
var sPage = getOption("eeScannerPage", mw.config.get("wgFormattedNamespaces")[2] + ":YMS/EagleEye"); // The page that will trigger the database scanner (usually only useful where User:YMS/EagleEye doesn't exist)
var sResultPage = getOption("eeScannerResultPage", mw.config.get("wgFormattedNamespaces")[2] + ":" + mw.config.get("wgUserName") + "/eagleeye.result"); // The default page where result lists are saved (on demand)
var sSaveWithMatches = getOption("eeScannerResultPageWithMatches", true); // Whether saving the result page should include matches (instead of titles only)
var sIgnoreRedirects = true; // With current scanner functionality, it's pointless to allow redirects
// Labels
var labels = {
genArticleNS: { en: "(Article)", de: "(Artikel)" },
genName: { en: "EagleEye", de: "EagleEye" },
genStartScanner: { en: "Start EagleEye database scanner", de: "EagleEye Datenbankscanner starten" },
genUnknownError: { en: "Unknown error. ", de: "Unbekannter Fehler." },
intLabelMissing: { en: "Internal error: Label missing: {0}", de: "Interner Fehler: Label {0} fehlt" },
lblAbort: { en: "Abort scan", de: "Scan abbrechen" },
lblAddRule: { en: "Add new rule", de: "Neue Regel hinzufügen" },
lblCancel: { en: "cancel", de: "abbrechen" },
lblChunkSize: { en: "Chunk size (in bytes)", de: "Chunk-Größe (in Byte)" },
lblInstalled: { en: "Script already is installed and active.", de: "Das Skript ist bereits installiert und aktiv." },
lblLimit: { en: "Limit results (0 = no limit)", de: "Ergebnisse begrenzen (0 = keine Begrenzung)" },
lblListAll: { en: "List all matches (instead of only the first one for every page)", de: "Alle Ergebnisse auflisten (statt nur dem ersten pro Seite)" },
lblListReplacements: { en: "Also list replacements (slower search, but better overview and possibility to preview Corrector changes)", de: "Auch Ersetzungen auflisten (langsamere Suche, aber besser Übersicht und Vorschaumöglichkeit für Corrector-Änderungen)" },
lblMatch: { en: "Match", de: "Match" },
lblName: { en: "Name", de: "Name" },
lblNamespaces: { en: "Namespaces", de: "Namensräume" },
lblNotes: { en: "Notes", de: "Anmerkungen" },
lblOptions: { en: "Options", de: "Optionen" },
lblProgress: { en: "{0}% ({1} of {2} bytes) scanned", de: "{0}% ({1} von {2} Bytes) gescannt" },
lblRemoveRule: { en: "remove", de: "löschen" },
lblRemoveRuleLong: { en: "Remove rule", de: "Regel löschen" },
lblRemoveRuleLongest: { en: "Delete rule {0}?", de: "Regel {0} löschen?" },
lblReplace: { en: "Replace", de: "Ersetze" },
lblRule: { en: "Rule", de: "Regel" },
lblRules: { en: "Rules", de: "Regeln" },
lblSaveResultsWMatches: { en: "include matches (instead of page list only)", de: "inkl. Matches (statt nur einer Liste der Seiten)" },
lblSaveResults: { en: "Save Results", de: "Ergebnisse speichern" },
lblSaveResultsOnPage: { en: "on page", de: "auf Seite" },
lblSaveRules: { en: "Save Ruleset", de: "Ruleset speichern" },
lblSaveRulesInfo: { en: "Attention: By this, you will perform an edit of page [[{0}}]], overwriting it completely.", de: "Achtung: Damit bearbeitest und überschreibst du die Seite [[{0}]]." },
lblScanDump: { en: "Scan dump", de: "Dump scannen" },
lblSearch: { en: "Search", de: "Suche" },
lblSelectAllNamespaces: { en: "Select/deselect all", de: "Alle/keinen auswählen" },
lblSelectAllRules: { en: "Activate/deactivate all rules", de: "Alle/keine Regel aktivieren" },
lblSelectDump: { en: "Select database dump file", de: "Datenbank-Dump-Datei wählen" },
lblSkip: { en: "Skip", de: "Überspringen" },
lblTest: { en: "Test", de: "Test" },
lblTitle: { en: "Title", de: "Titel" },
lblUseSkiplist: { en: "Use Skiplist", de: "Skiplist verwenden" },
saveRuleFailed: { en: "Error saving ruleset: {0}", de: "Fehler beim Speichern des Rulesets: {0}" },
saveRuleSuccess: { en: "Ruleset successfully saved.", de: "Ruleset erfolgreich gespeichert." },
saveRuleSummary: { en: "Save [[{0}|EagleEye]] ruleset changes", de: "[[{0}|EagleEye]]-Ruleset-Änderungen gespeichert" },
saveResultsFailed: { en: "Error saving result list: {0}", de: "Fehler beim Speichern der Ergebnisliste: {0}" },
saveResultsSuccess: { en: "Result list successfully saved.", de: "Ergebnisliste erfolgreich gespeichert." },
saveResultsSummary: { en: "Save [[{0}|EagleEye]] result list", de: "[[{0}|EagleEye]]-Ergebnisliste gespeichert" },
statAborted: { en: "Scan aborted. Found {0} pages in {1}.", de: "Scan abgebrochen. {0} Seiten in {1} gefunden." },
statCheckingRules: { en: "Checking Rules...", de: "Prüfe Regeln..." },
statChunkTooSmall: { en: "Error: Chunk size too small to process dump.", de: "Fehler: Chunk-Größe zu klein für diesen Dump." },
statFinished: { en: "Finished. Found {0} pages in {1}.", de: "Fertig. {0} Seiten in {1} gefunden." },
statInvalidChunkSize: { en: "Error: Chunk size invalid.", de: "Fehler: Chunk-Größe ungültig." },
statInvalidLimit: { en: "Error: Result limit invalid.", de: "Fehler: Ergebnis-Limit ungültig." },
statJSONfailed: { en: "Error: EagleEye Ruleset {0} could not be loaded: {1}", de: "Fehler: EagleEye-Ruleset {0} konnte nicht geladen werden: {1}" },
statNoFile: { en: "Error: No file selected.", de: "Fehler: Keine Datei ausgewählt." },
statRuleFailsTest: { en: "Error: Rule {0} ({1}) fails defined test.", de: "Fehler: Regel {0} ({1}) besteht den angegebenen Test nicht." },
statRuleInvalid: { en: "Error: Rule {0} ({1}) invalid: {2}", de: "Fehler: Regel {0} ({1}) ungültig: {2}" },
statRulesetMissing: { en: "Error: EagleEye ruleset empty.", de: "Fehler: Leeres EagleEye-Ruleset angegeben." },
statRuleUndefined: { en: "Error: Rule {0} ({1}) undefined.", de: "Fehler: Regel {0} ({1}) undefiniert." },
statScanningDump: { en: "Scanning {0} dump.", de: "Scanne Dump {0}." },
statStartScanning: { en: "Start scanning.", de: "Starte Scanvorgang." },
statUnsupBrowser: { en: "Error: Unsupported browser.", de: "Fehler: Browser nicht unterstützt." },
statUnsupDump: { en: "Error: Unsupported dump type.", de: "Fehler: Dump nicht unterstützt." },
statXRegExpFailed: { en: "Error: XRegExp library could not be loaded: {1}", de: "Fehler: XRegExp-Bibliothek konnte nicht geladen werden: {1}" }
};
// Internal variables
var SCANNER = 0;
var MARKER = 1;
var CORRECTOR = 2;
var rules;
var base;
var start = 0;
var stop = 0;
var nextText = "";
var subResultCount;
var resultList = [];
var startTime;
var running;
var api;
var colGreen = "#88FF88";
var colRed = "#FF8888";
// Startup - load ruleset file
$(document).ready(function() {
try {
$.getScript("http://tools.wmflabs.org/eagleeye/xregexp-all-min.js", function() {
try {
XRegExp.install("natives");
if (getOption("eeRulesetDbg", "").toString().length) {
// Debug mode - load rules from global JSON var eeRulesetDbg
init(getOption("eeRulesetDbg", ""));
} else {
// Production mode - load rules from specified JSON file
$.getJSON(mw.config.get("wgServer") + mw.config.get("wgScript") + "?title=" + mw.util.wikiUrlencode(ruleset) + "&action=raw&ctype=application/json").done(function(data) {
init(data);
}).fail(function(jqxhr, textStatus, error) {
mw.notify(getText("statJSONfailed").format(ruleset, textStatus + ": " + error));
});
}
} catch (e) {
mw.notify(getText("statXRegExpFailed").format(e));
}
}).fail(function(jqxhr, textStatus, error) {
mw.notify(getText("statXRegExpFailed").format(textStatus + ": " + error));
});
} catch (e) {
}
});
// String formatter
String.prototype.format = function() {
var i;
var s = this;
for (i = 0; i < arguments.length; i++) {
s = XRegExp.replace(s, XRegExp.cache("\\{" + i + "\\}", "gm"), arguments[i]);
}
return s;
};
// Corrector: Add button to edit mode
function addCorrectorButton() {
$("#wpTextbox1").wikiEditor("addToToolbar", {
section: "main",
group: "format",
tools: {
EagleEyeCorrector: {
label: getText("genName"),
type: "button",
icon: "//upload.wikimedia.org/wikipedia/commons/thumb/f/fb/PR_icon.png/22px-PR_icon.png",
action: {
type: "callback",
execute: function() {
scanEditWindow(this);
}
}
}
}
});
}
// Add the findings for the current page to the result list (scanner tool display)
function addPageResultsToDisplay(page) {
var pageResults = scanPage(page);
if (pageResults.matches.length > 0) {
var rule, i;
var count = 0;
var first = true;
resultList.push(pageResults);
for (rule in pageResults.matches) {
count += pageResults.matches[rule].length;
}
$("#eeResultTable").append($("<tr />", { id: "eeResult_" + subResultCount, "class": "eeResultTableDataLine" }));
$("#eeResult_" + subResultCount).append($("<td />", { "class": "eeResultTableTitleColumn", rowspan: count }).append('[[<a href="' + base + mw.util.wikiUrlencode(pageResults.title) + '">' + pageResults.title + '</a>]]'));
for (rule in pageResults.matches) {
if (! first) {
$("#eeResultTable").append($("<tr />", { id: "eeResult_" + subResultCount, "class": "eeResultTableDataLine" }));
}
first = false;
$("#eeResult_" + subResultCount).append($("<td />", { "class": "eeResultTableRuleColumn", rowspan: pageResults.matches[rule].length }).append(rules[rule].name));
for (i = 0; i < pageResults.matches[rule].length; i++) {
if (i !== 0) {
$("#eeResultTable").append($("<tr />", { id: "eeResult_" + subResultCount, "class": "eeResultTableDataLine" }));
}
$("#eeResult_" + subResultCount).append($("<td />", { "class": "eeResultTableMatchColumn" }).append(mw.html.escape(pageResults.matches[rule][i])));
$("#eeResult_" + subResultCount).append($("<td />", { "class": "eeResultTableReplaceColumn" }).append(mw.html.escape(pageResults.replaces[rule][i])));
subResultCount++;
}
}
}
return (sLimit === 0 || sLimit > resultList.length);
}
// Add results to the results page (wikitext export)
function addPageResultsToExport(pageResults, addMatches) {
var rule, i;
var text = "";
if (pageResults.matches.length > 0) {
text = "* [[{0}]]".format(pageResults.title);
if (addMatches) {
for (rule in pageResults.matches) {
var ruleMatches = "";
for (i = 0; i < pageResults.matches[rule].length; i++) {
if (i !== 0) {
ruleMatches += "; ";
}
ruleMatches += mw.html.escape(pageResults.matches[rule][i]);
}
text += " ({0}: {1})".format(rules[rule].name, ruleMatches);
}
}
text += "\n";
}
return text;
}
// Add a rule editor for the given line
function addRuleEditor(i) {
$("#eeRuleEditorTable").append($("<tr />", { id: "eeRuleEditor_" + i, "class": "eeRuleEditorTableDataLine" }));
$("#eeRuleEditor_" + i).append($("<td />", { "class": "eeRuleEditorTableCheckboxColumn" }).append($("<input />", { type: "checkbox", id: "eeRuleCB_" + i, checked: (rules[i].active === true) })));
$("#eeRuleEditor_" + i).append($("<td />", { "class": "eeRuleEditorTableNameColumn" }).append($("<input />", { type: "text", id: "eeRuleName_" + i, value: rules[i].name }).css("width", "100%")));
$("#eeRuleEditor_" + i).append($("<td />", { "class": "eeRuleEditorTableMatchColumn" }).append($("<input />", { type: "text", id: "eeRuleMatch_" + i, value: rules[i].match }).css("width", "100%")));
$("#eeRuleEditor_" + i).append($("<td />", { "class": "eeRuleEditorTableSkipColumn" }).append($("<input />", { type: "text", id: "eeRuleSkip_" + i, value: rules[i].skip }).css("width", "100%")));
$("#eeRuleEditor_" + i).append($("<td />", { "class": "eeRuleEditorTableReplaceColumn" }).append($("<input />", { type: "text", id: "eeRuleReplace_" + i, value: rules[i].replace }).css("width", "100%")));
$("#eeRuleEditor_" + i).append($("<td />", { "class": "eeRuleEditorTableTestColumn" }).append($("<input />", { type: "text", id: "eeRuleTest_" + i, value: rules[i].test }).css("width", "100%")));
$("#eeRuleEditor_" + i).append($("<td />", { "class": "eeRuleEditorTableNotesColumn" }).append($("<input />", { type: "text", id: "eeRuleNotes_" + i, value: rules[i].note }).css("width", "100%")));
$("#eeRuleEditor_" + i).append($("<td />", { "class": "eeRuleEditorTableRemoveColumn" }).append($("<input />", { type: "button", id: "eeRuleRemove_" + i, value: getText("lblRemoveRule") })));
// Check test on relevant changes
$("#eeRuleMatch_" + i + ",#eeRuleTest_" + i).change(function() {
if (applyRuleSettingsFromUI(i)) {
$("#eeRuleTest_" + i).css("background-color", (validateRule(i)) ? colGreen : colRed);
}
}).change();
// Button "remove"
$("#eeRuleRemove_" + i).click(function() {
applyRuleSettingsFromUI(i);
showConfirm(getText("lblRemoveRuleLong"), getText("lblRemoveRuleLongest").format(rules[i].name), getText("lblRemoveRule"), getText("lblCancel"), function () {
rules.splice(i, 1);
loadRuleEditor();
});
});
}
// Add a collapsible section to the HTML
function addUISection(sectionID, bodyID, sectionContent, bodyContent, collapsed) {
$("#mw-content-text").append($("<div/>", { id: sectionID, "class": "mw-collapsible eeSection" }).addClass((collapsed) ? "mw-collapsed" : "").append($("<b />").append(sectionContent)).append($("<div />", { id: bodyID, "class": "mw-collapsible-content" }).append(bodyContent)));
}
// Get the settings for a certain rule from user's input
function applyRuleSettingsFromUI(i) {
try {
rules[i].active = $("#eeRuleCB_" + i).prop("checked");
rules[i].name = $("#eeRuleName_" + i).val();
rules[i].match = $("#eeRuleMatch_" + i).val();
rules[i].skip = $("#eeRuleSkip_" + i).val();
rules[i].replace = $("#eeRuleReplace_" + i).val();
rules[i].test = $("#eeRuleTest_" + i).val();
rules[i].note = $("#eeRuleNotes_" + i).val();
} catch (e) {
return false;
}
return true;
}
// Get rules and other settings from user's input
function applySettingsFromUI() {
var i;
// Rules
for (i = 0; i < rules.length; i++) {
if (! applyRuleSettingsFromUI(i)) {
setStatus(getText("statRuleInvalid").format(i, rules[i].name, getText("genUnknownError")), false);
return;
}
}
// Options
useSkiplist[SCANNER] = $("#eeOptionCB_useSkiplist").prop("checked");
sListAll = $("#eeOptionCB_listAll").prop("checked");
sListReplacements = $("#eeOptionCB_listReplacements").prop("checked");
sChunkSize = Number($("#eeOptionCB_chunkSize").val());
if (! $.isNumeric(sChunkSize) || sChunkSize <= 0) {
setStatus(getText("statInvalidChunkSize"), false);
return;
}
sLimit = Number($("#eeOptionCB_limit").val());
if (! $.isNumeric(sLimit) || sLimit < 0) {
setStatus(getText("statInvalidLimit"), false);
return;
}
// Namespaces
namespaces[SCANNER] = [];
$("input[id^=eeNamespaceCB_]").each(function() {
if ($(this).prop("checked")) {
namespaces[SCANNER].push(Number($(this).attr("id").substring($(this).attr("id").indexOf("_") + 1)));
}
});
}
// Save page via API
function editPage(title, text, summary, successMsg, failMsg) {
api.post({ action: "edit", title: title, text: text, summary: summary, token: mw.user.tokens.get("csrfToken") }).done(function(data) {
if (data && data.edit && data.edit.result === "Success") {
mw.notify(successMsg);
} else if (data && data.error) {
mw.notify(failMsg.format(data.error.code + ": " + data.error.info));
} else {
mw.notify(failMsg.format(getText("genUnknownError")));
}
}).fail(function(error) {
mw.notify(failMsg.format(error));
});
}
// Enable/disable the "Save Results" button
function enableSaveResultsButton() {
var enable = ! running && true && resultList.length > 0;
$("#eeSaveResults").attr("disabled", ! enable);
}
// Enable/disable the "Save Rules" button
function enableSaveRulesButton() {
var enable = true && rules.length > 0;
$("#eeSaveRules").attr("disabled", ! enable);
}
// Enable/disable the "Search/Abort" button
function enableSearchButton() {
var enable = document.getElementById("eeFile").files.length > 0;
$("#eeSearch").attr("disabled", ! enable);
}
// Get default language (user language, if available, else project language, if available, else first defined language)
function getDefaultLang(supportedLanguages) {
if ($.inArray(mw.config.get("wgUserLanguage"), supportedLanguages) !== -1) {
return mw.config.get("wgUserLanguage");
} else if ($.inArray(mw.config.get("wgContentLanguage"), supportedLanguages) !== -1) {
return mw.config.get("wgContentLanguage");
} else {
return supportedLanguages[0];
}
}
// Get duration of a scan in format mm:ss
function getDuration() {
var msec = ($.now() - startTime);
var sec = ((Math.floor(msec / 1000) % 60 < 10) ? "0" : "") + Math.floor(msec / 1000) % 60;
var min = ((Math.floor(msec / 1000 / 60) < 10) ? "0" : "") + Math.floor(msec / 1000 / 60);
return min + ":" + sec;
}
// Load a user-defined configuration variable or the default value
function getOption(name, defaultvalue) {
return (typeof window[name] === "undefined") ? defaultvalue : window[name];
}
// Internationalisation of a label
function getText(label) {
if (labels[label] === null || labels[label][lang] === null) {
return getText("intLabelMissing").format(label);
}
return labels[label][lang];
}
// Initialise scanner, marker, corrector and additional stuff (scanner portlet, etc.)
function init(data) {
var i;
if (! window.File || ! window.FileReader || ! window.Blob) {
setStatus(getText("statUnsupBrowser"), false);
return;
}
rules = data;
if (typeof rules === "undefined" || rules === null || rules.length === 0) {
mw.notify(getText("statRulesetMissing"));
return;
}
if (activated[SCANNER]) {
mw.util.addPortletLink("p-tb", mw.util.getUrl(sPage), getText("genName"), "t-eagleeye", getText("genStartScanner"));
if (mw.config.get("wgPageName") === sPage && mw.config.get("wgAction") === "view") {
// Scanner view
mw.util.addCSS("div.eeSection { border: 1px solid black; padding: 8px; }");
mw.util.addCSS(".eeResultTable td { border-top: 1px solid #CCCCCC; padding: 4px }");
mw.util.addCSS("tr.eeRuleEditorTableDataLine:hover { background-color: #CCCCCC }");
mw.loader.using([ "mediawiki.api" ], function() {
api = new mw.Api();
});
loadScannerUI();
}
}
if (activated[MARKER] && $.inArray(mw.config.get("wgNamespaceNumber"), namespaces[MARKER]) !== -1 && mw.config.get("wgAction") === "view") {
// Article view: Marker
mw.util.addCSS("span.eeMarker { " + mStyle + " }");
scanView();
}
if (activated[CORRECTOR] && $.inArray(mw.config.get("wgNamespaceNumber"), namespaces[CORRECTOR]) !== -1 && $.inArray(mw.config.get("wgAction"), [ "edit", "submit" ]) !== -1) {
// Edit view - add button for Corrector
mw.loader.using("ext.wikiEditor", function () {
addCorrectorButton();
});
}
}
// (Re)loads the rule editor UI
function loadRuleEditor() {
var i;
var table = $("<table />", { id: "eeRuleEditorTable", "class": "eeRuleEditorTable", width: "100%" });
var header = $("<tr />", { id: "eeRuleEditorTableHeaderLine", "class": "eeRuleEditorTableHeaderLine" });
table.append(header);
header.append($("<th />", { "class": "eeRuleEditorTableCheckboxColumn" }));
header.append($("<th />", { "class": "eeRuleEditorTableNameColumn", width: "5%" }).append(getText("lblName")));
header.append($("<th />", { "class": "eeRuleEditorTableMatchColumn", width: "40%" }).append(getText("lblMatch")));
header.append($("<th />", { "class": "eeRuleEditorTableSkipColumn", width: "30%" }).append(getText("lblSkip")));
header.append($("<th />", { "class": "eeRuleEditorTableReplaceColumn", width: "5%" }).append($("<i />").append(getText("lblReplace"))));
header.append($("<th />", { "class": "eeRuleEditorTableTestColumn", width: "10%" }).append($("<i />").append(getText("lblTest"))));
header.append($("<th />", { "class": "eeRuleEditorTableNotesColumn", width: "10%" }).append($("<i />").append(getText("lblNotes"))));
header.append($("<th />", { "class": "eeRuleEditorTableCopyColumn" }));
$("#eeRulesPaneContent").html(table);
for (i = 0; i < rules.length; i++) {
addRuleEditor(i);
}
}
// Add the GUI elements for the scanner
function loadScannerUI() {
var i, id;
// Collapse pre-defined installation section and set a marker
if ($("#eeInstallation").length) {
$("#eeInstallation").find(".mw-collapsible-content").prepend($("<div />", { id: "eeInstallationNotice" }).append(getText("lblInstalled")));
$("#eeInstallationNotice").css({ "background-color": "#44DD44", "padding": "5px", "margin": "5px" });
$("#eeInstallation").addClass("mw-collapsed");
}
addUISection("eeRulesPane", "eeRulesPaneContent", getText("lblRules"), "", true);
loadRuleEditor();
// Buttons
$("#eeRulesPane").append($("<div />", { "class": "mw-collapsible-content" }).append($("<input />", { type: "button", id: "eeSelectAllRules", name: "eeSelectAllRules", val: getText("lblSelectAllRules") })).append($("<input />", { type: "button", id: "eeAddRule", name: "eeAddRule", val: getText("lblAddRule") })).append($("<input />", { type: "button", id: "eeSaveRules", name: "eeSaveRules", val: getText("lblSaveRules") })).append($("<i />").append(getText("lblSaveRulesInfo").format(ruleset))));
$("#eeSelectAllRules").click(function() {
selectDeselectAll($("input[id^=eeRuleCB_]"));
});
$("#eeAddRule").click(function() {
// Add new (empty) rule to the rule editor
rules.push({ active: true });
addRuleEditor(rules.length - 1);
});
$("#eeSaveRules").click(function() {
saveRuleset();
});
// Options
addUISection("eeOptionsPane", "eeOptionsPaneContent", getText("lblOptions"), null, true);
$("#eeOptionsPaneContent").append($("<div/>").append($("<input />", { type: "checkbox", id: "eeOptionCB_useSkiplist", checked: (useSkiplist[SCANNER]) })).append($("<label />", { "for": "eeOptionCB_useSkiplist" }).append(getText("lblUseSkiplist"))));
$("#eeOptionsPaneContent").append($("<div/>").append($("<input />", { type: "checkbox", id: "eeOptionCB_listAll", checked: (sListAll) })).append($("<label />", { "for": "eeOptionCB_listAll" }).append(getText("lblListAll"))));
$("#eeOptionsPaneContent").append($("<div/>").append($("<input />", { type: "checkbox", id: "eeOptionCB_listReplacements", checked: (sListReplacements) })).append($("<label />", { "for": "eeOptionCB_listReplacements" }).append(getText("lblListReplacements"))));
$("#eeOptionsPaneContent").append($("<div/>").append($("<input />", { type: "number", id: "eeOptionCB_chunkSize", value: sChunkSize, min: "1" })).append($("<label />", { "for": "eeOptionCB_chunkSize" }).append(getText("lblChunkSize"))));
$("#eeOptionsPaneContent").append($("<div/>").append($("<input />", { type: "number", id: "eeOptionCB_limit", value: sLimit, min: "0" })).append($("<label />", { "for": "eeOptionCB_limit" }).append(getText("lblLimit"))));
// Namespaces
addUISection("eeNamespacePane", "eeNamespacePaneContent", getText("lblNamespaces"), $("<div />", { id: "eeNamespacesList" }).css("column-width", "200px"), true);
for (id in mw.config.get("wgFormattedNamespaces")) {
var name = (mw.config.get("wgFormattedNamespaces")[id] === "") ? getText("genArticleNS") : mw.config.get("wgFormattedNamespaces")[id];
$("#eeNamespacesList").append($("<div/>").append($("<input />", { type: "checkbox", id: "eeNamespaceCB_" + id, checked: ($.inArray(Number(id), namespaces[SCANNER]) !== -1) })).append($("<label />", { "for": "eeNamespaceCB_" + id }).append(name)));
}
$("#eeNamespacePaneContent").append($("<div />").append($("<input />", { type: "button", id: "eeSelectAllNamespaces", name: "eeSelectAllNamespaces", val: getText("lblSelectAllNamespaces") })));
$("#eeSelectAllNamespaces").click(function() {
selectDeselectAll($("input[id^=eeNamespaceCB_]"));
});
// Search
addUISection("eeSearchPane", "eeSearchPaneContent", getText("lblSearch"), $("<label />", { "for": "eeFile" }).append(getText("lblSelectDump")));
$("#eeSearchPaneContent").append($("<div />").append($("<input />", { type: "file", id: "eeFile", name: "eeFile" }), false));
$("#eeSearchPaneContent").append($("<div />").append($("<input />", { type: "button", id: "eeSearch", name: "eeSearch" })));
// Output section
$("#eeSearchPaneContent").append($("<div/>", { id: "eeStatus" }));
$("#eeSearchPaneContent").append($("<progress />", { id: "eeProgressBar", value: "0" }).css("width", "100%"));
$("#eeSearchPaneContent").append($("<div/>", { id: "eeProgress" }));
$("#eeSearchPaneContent").append($("<br/>"));
$("#eeSearchPaneContent").append($("<output/>", { id: "eeResults" }));
$("#eeSearchPaneContent").append($("<br/>"));
// Save button
$("#eeSearchPaneContent").append($("<div />").append($("<input />", { type: "button", id: "eeSaveResults", name: "eeSaveResults", value: getText("lblSaveResults") })).append(getText("lblSaveResultsOnPage")).append($("<input />", { type: "text", id: "eeSaveResultsPage", val: sResultPage })).append($("<input />", { type: "checkbox", id: "eeSaveResultPageWithMatches", checked: sSaveWithMatches })).append($("<label />", { "for": "eeSaveResultPageWithMatches" }).append(getText("lblSaveResultsWMatches"))));
// Assure collapsible sections are made collapsible
mw.loader.using("mediawiki.page.ready", function () {
//if ($("#eeInstallation").length) {
// $("#eeInstallation").collapse("mw-collapsed");
//}
$(".mw-collapsible").makeCollapsible();
});
// File select: Enable Search
$("#eeFile").change(function() {
enableSearchButton();
});
// Button: "Search"/"Abort"
$("#eeSearch").click(function() {
if (running) {
setStatus(getText("statAborted").format(resultList.length, getDuration()), false);
} else {
applySettingsFromUI();
scanFile();
}
setRunning(! running);
});
// Button: "Save Results"
$("#eeSaveResults").click(function() {
var i;
var text = "";
var addMatches = $("#eeSaveResultPageWithMatches").prop("checked");
for (i = 0; i < resultList.length; i++) {
text += addPageResultsToExport(resultList[i], addMatches);
}
editPage($("#eeSaveResultsPage").val(), text, getText("saveResultsSummary").format($("#eeSaveResultsPage").val()), getText("saveResultsSuccess"), getText("saveResultsFailed"));
});
enableSearchButton();
enableSaveRulesButton();
setRunning(false);
}
// Finished reading a dump chunk, start reading next one
function readNextChunk(e, reader, file) {
if (! running || e.target.readyState !== FileReader.DONE || file === null || ! file) {
setRunning(false);
return;
}
setProgress(stop, file.size);
// Trim chopped-of tags
var text = e.target.result;
if (start > 0) {
text = "<mediawiki>" + nextText + text;
}
var lastCloseText = text.lastIndexOf("</page>");
if (lastCloseText < 0) {
setStatus(getText("statChunkTooSmall"), false);
return;
}
nextText = text.substring(lastCloseText + "</page>".length);
text = text.substring(0, lastCloseText + "</page>".length) + "</mediawiki>";
// Detect base
if (start === 0) {
// jQuery can't read tags named "base" for some reason, so load this manually
var baseStart = text.indexOf("<base>");
base = text.substring(baseStart + "<base>".length, text.indexOf("</base>"));
base = base.substring(0, base.lastIndexOf("/") + 1);
if (baseStart < 0 || base.length === 0) {
setStatus(getText("statUnsupDump"), false);
return;
}
setStatus(getText("statScanningDump").format(base), true);
}
// Search
$(text).find("page").each(function() {
// Check namespace
if ($.inArray(Number($(this).find("ns").text()), namespaces[SCANNER]) === -1) {
return;
}
// Check redirect status
if (sIgnoreRedirects && $(this).find("redirect").length > 0) {
return;
}
return addPageResultsToDisplay($(this));
});
// Next chunk
if (stop < file.size && (sLimit === 0 || sLimit > resultList.length)) {
start = start + sChunkSize;
stop = stop + sChunkSize;
reader.readAsText(file.slice(start, stop));
} else {
setStatus(getText("statFinished").format(resultList.length, getDuration()), true);
setRunning(false);
}
}
// Save ruleset definition to specified file
function saveRuleset() {
var i;
// Convert rules to JSON
for (i = 0; i < rules.length; i++) {
if (! applyRuleSettingsFromUI(i)) {
mw.notify(getText("statRuleInvalid").format(i, rules[i].name, getText("genUnknownError")));
return;
}
}
if (! validateRuleset()) {
mw.notify($("#eeStatus").text());
return;
}
// Save rules
editPage(ruleset, JSON.stringify(rules, null, 2), getText("saveRuleSummary").format(sPage), getText("saveRuleSuccess"), getText("saveRuleFailed"));
}
// Corrector: Edit window processing
function scanEditWindow(context) {
var i;
var changed = false;
var a = context.clickedElement;
for (i = 0; i < rules.length; i++) {
if (rules[i].active === true) {
if (! changed && $("#wpTextbox1").text().replace(XRegExp.cache(rules[i].match, "g")) !== null) {
changed = true;
}
$("#wpTextbox1").text(XRegExp.replace($("#wpTextbox1").text(), XRegExp.cache(rules[i].match, "g"), rules[i].replace));
}
}
if (! a || ! a.nodeType || a.nodeName === "IMG") {
$((a && a.nodeType) ? a : "img[rel=EagleEyeCorrector]").css("background-color", (changed) ? colGreen : colRed);
}
}
// Perform dump scan
function scanFile() {
var file = document.getElementById("eeFile").files[0];
start = 0;
stop = start + sChunkSize;
$("#eeResults").html($("<table />", { id: "eeResultTable", "class": "eeResultTable" }));
var header = $("<tr />", { id: "eeResultTableHeaderLine", "class": "eeResultTableHeaderLine" });
$("#eeResultTable").append(header);
header.append($("<th />", { "class": "eeResultTableTitleColumn" }).append(getText("lblTitle")));
header.append($("<th />", { "class": "eeResultTableRuleColumn" }).append(getText("lblRule")));
header.append($("<th />", { "class": "eeResultTableMatchColumn" }).append(getText("lblMatch")));
header.append($("<th />", { "class": "eeResultTableReplaceColumn" }).append(getText("lblReplace")));
setStatus(getText("statCheckingRules"), true);
setProgress(0, file.size);
resultList = [];
subResultCount = 0;
startTime = $.now();
if (! validateRuleset()) {
return;
}
setStatus(getText("statStartScanning"), true);
if (file === null || ! file) {
setStatus(getText("statNoFile"), false);
return;
}
var reader = new FileReader();
reader.onloadend = function(e) {
readNextChunk(e, reader, file);
};
reader.readAsText(file.slice(start, stop));
}
// Iterate all rules and match given text to it
function scanPage(object) {
var i, j;
var pageResults = { title: object.find("title").text(), matches: [], replaces: [] };
for (i = 0; i < rules.length; i++) {
if (rules[i].active === true) {
// Match rule if active
var match = object.find("text").text().match(XRegExp.cache(rules[i].match, "g"));
if (match !== null) {
// Check results for skiplist match
for (j = 0; j < match.length; j++) {
if (! useSkiplist[SCANNER] || rules[i].skip === undefined || rules[i].skip === XRegExp.cache("") || rules[i].skip.length === 0 || ! match[j].match(XRegExp(rules[i].skip))) {
var replacement = (sListReplacements) ? XRegExp.replace(match[j], XRegExp.cache(rules[i].match, "g"), rules[i].replace, "one") : "";
if (pageResults.matches[i] === undefined) {
pageResults.matches[i] = [match[j]];
pageResults.replaces[i] = [replacement];
} else {
pageResults.matches[i].push(match[j]);
pageResults.replaces[i].push(replacement);
}
if (! sListAll) {
return pageResults;
}
}
}
}
}
}
return pageResults;
}
// Marker: Mark findings in article view
function scanView() {
var i;
$("#mw-content-text").children(":not(.diff):not(.diff *)").each(function() {
if ($(this).html() !== null) {
var text = $(this).html();
for (i = 0; i < rules.length; i++) {
if (rules[i].active === true) {
text = text.replace(XRegExp.cache(rules[i].match, "g"), '<span class="eeMarker">$&<sup>' + mPrefix + rules[i].name + '</sup></span>');
}
}
$(this).html(text);
}
});
}
// Select or deselect all of the given checkboxes
function selectDeselectAll($boxes) {
var allActivated = true;
$boxes.each(function() {
if (! $(this).prop("checked")) {
allActivated = false;
return false;
}
});
$boxes.prop("checked", ! allActivated);
}
// Sets text of progress display and bar
function setProgress(value, max) {
if (value === 0) {
$("#eeProgress").text("");
$("#eeProgressBar").attr({ value: value, max: max });
} else {
var percentage = (Math.min(value, max) / max) * 100;
$("#eeProgress").text(getText("lblProgress").format(percentage.toFixed(0), Math.min(value, max), max));
$("#eeProgressBar").attr("value", value);
}
}
// Sets the running state and changes start/abort button's label and behaviour
function setRunning(run) {
running = run;
if (running) {
$("#eeSearch").val(getText("lblAbort"));
$("#eeProgressBar").show();
} else {
$("#eeSearch").val(getText("lblScanDump"));
$("#eeProgressBar").hide();
}
enableSaveResultsButton();
}
// Sets text of status section
function setStatus(text, okay) {
$("#eeStatus").text(text);
$("#eeStatus").css("background-color", (okay) ? colGreen : colRed);
}
// Display a message box with two options
function showConfirm(title, text, doText, cancelText, callback) {
$("<div />").dialog({
title: title,
modal: true,
buttons: [{
text: doText,
click: function() {
callback();
$(this).remove();
}
}, {
text: cancelText,
click: function() {
$(this).remove();
}
}],
close: function (event, ui) {
$(this).remove();
}
}).text(text).parent().addClass("alert");
}
// Validate a single RegEx rule
function validateRule(i) {
if (rules[i].match === null) {
setStatus(getText("statRuleUndefined").format(i, rules[i].name), false);
return false;
}
try {
if (rules[i].test !== null && rules[i].test.length > 0) {
// Check standard test case
if (rules[i].test.match(XRegExp(rules[i].match, "g")) === null) {
setStatus(getText("statRuleFailsTest").format(i, rules[i].name), false);
return false;
} else {
setStatus("", true);
}
} else {
// Perform dummy test to check formal validity
XRegExp.test("dummy", rules[i].match);
}
} catch (e) {
setStatus(getText("statRuleInvalid").format(i, rules[i].name, e), false);
return false;
}
return true;
}
// Validate RegEx rule set
function validateRuleset() {
var i;
for (i = 0; i < rules.length; i++) {
if (! validateRule(i)) {
return false;
}
}
return true;
}
})();
// </nowiki>