Unnaschied vunde Gschischde vun "Middawaida:YMS/eagleeye.js"
Inhalt gelöscht Inhalt hinzugefügt
YMS (Dischbediere | Baidräsch) EagleEye v0.1 |
(kän Unnaschied)
|
Iwwa'awaidung vun 15:45, 25. Mai 2013
/**
* EagleEye
* Tool for searching, spotting and correcting hard-to-sport errors
* For more detailed documentation see [[de:User:YMS/EagleEye]]
* <nowiki> ([[bugzilla:8761]])
*/
(function() {
// Options and Ruleset
var rules = getOption("eeRules", []); // The rule set
var lang = getOption("eeLang", "en"); // Language (currently supported: en)
var activateScanner = getOption("eeActivateScanner", true); // Activate database scanner (portlet and tool itself)
var activateScript = getOption("eeActivateScript", true); // Activate correction script (button and script itself)
var activateMarker = getOption("eeActivateMarker", true); // Activate marker tool
var namespaces = getOption("eeNamespaces", [0]); // Namespaces for marker and correction script
var useSkiplist = getOption("eeUseSkiplist", true); // Whether the skiplist should be used for the database scanner
var chunkSize = getOption("eeChunkSize", 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 markerStyle = getOption("eeMarkerStyle", "background-color: #FF9191;"); // CSS style for the marker highlighting (more can be defined for span.eeMarker in user CSS
var markerPrefix = getOption("eeMarkerPrefix", "ee_"); // Prefix for markers (e.g. "ee_" will result in markers like "ee_Doppelwort", may be set to "" for no prefix)
var scannerPage = getOption("eeScannerPage", mw.config.get("wgFormattedNamespaces")[2] + ":YMS/EagleEye"); // The page that will trigger the database scanner
var ignoreRedirects = true; // With current functionality, it's pointless to allow redirects
// Labels
var labels = {
generalArticleNamespace: { en: "(Article)" },
generalName: { en: "EagleEye" },
generalStartScanner: { en: "Start EagleEye database scanner" },
internalLabelMissing: { en: "Internal error: Label missing: {0}" },
statusAborted: { en: "Scan aborted." },
statusCheckingRules: { en: "Checking Rules..." },
statusChunkSizeTooSmall: { en: "Error: Chunk size too small to process dump." },
statusFinished: { en: "Finished. Found {0} pages in {1}:{2}." },
statusInvalidChunkSize: { en: "Error: Chunk size invalid." },
statusNoFile: { en: "Error: No file selected." },
statusRuleFailsTest: { en: "Error: Rule {0} ({1}) fails defined test." },
statusRuleInvalid: { en: "Error: Rule {0} ({1}) invalid: {2}"},
statusRulesetMissing: { en: "Error: Ruleset missing." },
statusRuleUndefined: { en: "Error: Rule {0} ({1}) undefined." },
statusScanningDump: { en: "Scanning {0} dump." },
statusStartScanning: { en: "Start scanning." },
statusUnsupportedBrowser: { en: "Error: Unsupported browser." },
statusUnsupportedDump: { en: "Error: Unsupported dump type." },
lblAbort: { en: "Abort scan" },
lblNamespaces: { en: "Namespaces" },
lblOptions: { en: "Options" },
lblRules: { en: "Rules" },
lblScanDump: { en: "Scan dump" },
lblSearch: { en: "Search" },
lblSelectDump: { en: "Select database dump file" },
};
// Internal variables
var base;
var start = 0;
var stop = 0;
var nextText = "";
var results;
var startTime;
var aborted = false;
// String formatter
String.prototype.format = function() {
var s = this;
for (var i = 0; i < arguments.length; i++) {
s = s.replace(new RegExp("\\{" + i + "\\}", "gm"), arguments[i]);
}
return s;
};
// Internationalisation of a label
function txt(label) {
if (labels[label] == null || labels[label][lang] == null) {
return txt("internalLabelMissing").format(label);
}
return labels[label][lang];
}
// Load a user-defined configuration variable or the given default
function getOption(name, defaultvalue) {
return (typeof window[name] === "undefined") ? defaultvalue : window[name];
}
// Prints a new line in the result section
function printLine(text) {
$("#eeResults").append($("<span />").append(text).append("<br />"));
}
// Sets text of progress display
function setProgress(text) {
$("#eeProgress").text(text);
}
// Sets text of status section
function setStatus(text) {
$("#eeStatus").text(text);
}
// Add a new finding to the results section
function addResult(title, rule, match, base) {
printLine('[[<a href="' + base + encodeURI(title) + '">' + title + '</a>]] (' + rules[rule].name + ': ' + $("<textarea />").html(match).html() + ')');
results++;
}
// Iterate all rules and match given text to it
function searchByRules(object) {
for (var i = 0; i < rules.length; i++) {
if (rules[i].inactive == null || rules[i].inactive == false) {
// Match rule if not inactive
var match = object.find("text").text().match(new RegExp(rules[i].match.source, "g"));
if (match != null) {
// Check results for skiplist match
for (var j = 0; j < match.length; j++) {
if (! useSkiplist || rules[i].skip === undefined || ! match[j].match(rules[i].skip)) {
addResult(object.find("title").text(), i, match[j], base);
return;
}
}
}
}
}
}
// Validate RegEx rule set (and apply activation state)
function checkRules() {
for (var i = 0; i < rules.length; i++) {
// Activate/deactivate rule per user selection
rules[i].inactive = ! $("#eeRuleCB_" + i).attr("checked");
// Check rule
if (rules[i].match === null) {
setStatus(txt("statusRuleUndefined").format(i, rules[i].name));
return false;
}
try {
if (rules[i].test != null) {
// Check standard test case
if (rules[i].test.match(rules[i].match) === null) {
setStatus(txt("statusRuleFailsTest").format(i, rules[i].name));
return false;
}
} else {
// Perform dummy test to check formal validity
"dummy".match(rules[i].match);
}
} catch (e) {
setStatus(txt("statusRuleInvalid").format(i, rules[i].name, e));
return false;
}
}
return true;
}
// Edit window processing
function scanEditWindow() {
var changed = false;
var a = this.clickedElement;
for (var i = 0; i < rules.length; i++) {
if (! changed) {
if ($("#wpTextbox1").text().match(rules[i].match) != null) {
changed = true;
}
}
$("#wpTextbox1").text($("#wpTextbox1").text().replace(new RegExp(rules[i].match.source, "g"), rules[i].replace));
}
if (changed && (! a || ! a.nodeType || a.nodeName == "IMG")) {
$((a && a.nodeType) ? a : "img[rel=" + txt("generalName") + "]").css("backgroundColor", changed ? "#DEF740" : "");
}
};
// Add button to edit mode
function addButton() {
$("#wpTextbox1").wikiEditor("addToToolbar", {
section: "main",
group: "format",
tools: {
regExEdit: {
label: txt("generalName"),
type: "button",
icon: "//upload.wikimedia.org/wikipedia/commons/thumb/f/fb/PR_icon.png/22px-PR_icon.png",
action: {
type: "callback",
execute: function() {
scanEditWindow();
}
}
}
}
});
}
// Mark findings in article view
function markView() {
$("#mw-content-text").children(":not(.diff)").each(function() {
if ($(this).html() != null) {
for (var i = 0; i < rules.length; i++) {
if (rules[i].inactive == null || rules[i].inactive == false) {
$(this).html($(this).html().replace(new RegExp(rules[i].match.source, "g"), '<span class="eeMarker">$&<sup>' + markerPrefix + rules[i].name + '</sup></span>'));
}
}
}
});
}
// Add a collapsible section to the HTML
function addSection(sectionID, bodyID, sectionContent, bodyContent, collapsed) {
$("#mw-content-text").append($("<div/>", { id: sectionID, class: "mw-collapsible eeSection" }).addClass((collapsed) ? "mw-collapsed" : "").append("<b>" + sectionContent + "</b>").append($("<div />", { id: bodyID, class: "mw-collapsible-content" }).append(bodyContent)));
}
// Add a rule editor for the given line
function addRuleEditor(i) {
var checkbox = $("<input />", { type: "checkbox", id: "eeRuleCB_" + i, checked: (rules[i].inactive == null || rules[i].inactive == false) });
$("#eeRulesPaneContent").append($("<div/>").append(checkbox).append(rules[i].name));
}
// Add the GUI elements for the scanner
function loadScannerUI() {
// Rules
addSection("eeRulesPane", "eeRulesPaneContent", txt("lblRules"), null, true);
for (var i = 0; i < rules.length; i++) {
addRuleEditor(i);
}
// Options
addSection("eeOptionsPane", "eeOptionsPaneContent", txt("lblOptions"), null, true);
$("#eeOptionsPaneContent").append($("<div/>").append($("<input />", { type: "checkbox", id: "eeOptionCB_useSkiplist", checked: (useSkiplist) })).append("use Skiplist"));
$("#eeOptionsPaneContent").append($("<div/>").append($("<input />", { type: "number", id: "eeOptionCB_chunkSize", value: chunkSize, min: 0 })).append("Chunk size"));
// Namespaces
addSection("eeNamespacePane", "eeNamespacePaneContent", txt("lblNamespaces"), null, true);
for (var id in mw.config.get("wgFormattedNamespaces")) {
var name = (mw.config.get("wgFormattedNamespaces")[id] === "") ? txt("generalArticleNamespace") : mw.config.get("wgFormattedNamespaces")[id];
$("#eeNamespacePaneContent").append($("<div/>").append($("<input />", { type: "checkbox", id: "eeNamespaceCB_" + id, checked: ($.inArray(Number(id), namespaces) != -1) })).append(name));
}
// Search
//addSection("eeSearchPane", "eeSearchPaneContent", txt("lblSearch"), $("<input />", { type: "file", id: "eeFile", name: "eeFile" }), false);
//$("#eeSearchPaneContent").append($("<input />", { type: "button", id: "eeSearch", name: "eeSearch", value: txt("lblScanDump") }));
addSection("eeSearchPane", "eeSearchPaneContent", txt("lblSearch"), txt("lblSelectDump"));
$("#eeSearchPaneContent").append($("<div />").append($("<input />", { type: "file", id: "eeFile", name: "eeFile" }), false));
$("#eeSearchPaneContent").append($("<br/>"));
$("#eeSearchPaneContent").append($("<div />").append($("<input />", { type: "button", id: "eeSearch", name: "eeSearch", value: txt("lblScanDump") })));
// Output section
$("#eeSearchPaneContent").append($("<div/>", { id: "eeStatus" }));
$("#eeSearchPaneContent").append($("<div/>", { id: "eeProgress" }));
$("#eeSearchPaneContent").append($("<div/>", { id: "eeAbortDiv" }).append($("<a />", { id: "eeAbortLink" }).append(txt("lblAbort"))));
$("#eeAbortDiv").hide();
$("#eeSearchPaneContent").append($("<br/>"));
$("#eeSearchPaneContent").append($("<output/>", { id: "eeResults" }));
$("#eeAbortLink").click(function() {
setStatus(txt("statusAborted"));
aborted = true;
$("eeAbortDiv").hide();
});
$("#eeSearch").click(function() {
// Get options from UI
useSkiplist = $("#eeOptionCB_useSkiplist").attr("checked");
chunkSize = Number($("#eeOptionCB_chunkSize").val());
if (isNaN(chunkSize) || chunkSize <= 0) {
setStatus(txt("statusInvalidChunkSize"));
return;
}
namespaces = [];
$("input[id^=eeNamespaceCB_]").each(function() {
if ($(this).attr("checked")) {
namespaces.push(Number($(this).attr("id").substring($(this).attr("id").indexOf("_") + 1)));
}
});
// Search
scanFile();
});
}
// Perform dump scan
function scanFile() {
var reader = new FileReader();
var file = document.getElementById("eeFile").files[0];
stop = start + chunkSize;
$("#eeResults").text("");
$("#eeAbortDiv").show();
setStatus(txt("statusCheckingRules"));
setProgress("");
results = 0;
aborted = false;
startTime = new Date();
if (! checkRules()) {
return;
}
setStatus(txt("statusStartScanning"));
reader.onloadend = function(e) {
if (aborted || e.target.readyState != FileReader.DONE || file == null || ! file) {
return;
}
var percentage = (Math.min(stop, file.size) / file.size) * 100;
setProgress(percentage.toFixed(0) + "% (" + Math.min(stop, file.size) + " of " + file.size + " bytes)");
// 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(txt("statusChunkSizeTooSmall"));
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(txt("statusUnsupportedDump"));
return;
}
setStatus(txt("statusScanningDump").format(base));
}
// Search
$(text).find("page").each(function() {
// Check namespace
if ($.inArray(Number($(this).find("ns").text()), namespaces) == -1) {
return;
}
// Check redirect status
if (ignoreRedirects && $(this).find("redirect").length > 0) {
return;
}
searchByRules($(this));
});
// Next chunk
if (stop < file.size) {
start = start + chunkSize;
stop = stop + chunkSize;
var blob = file.slice(start, stop);
reader.readAsText(blob);
} else {
var msec = ((new Date()) - startTime);
var sec = ((Math.floor(msec / 1000) < 10) ? "0" : "") + Math.floor(msec / 1000);
var min = ((Math.floor(msec / 1000 / 60) < 10) ? "0" : "") + Math.floor(msec / 1000 / 60);
setStatus(txt("statusFinished").format(results, min, sec));
$("#eeAbortDiv").hide();
}
};
if (file == null || ! file) {
setStatus(txt("statusNoFile"));
return;
}
var blob = file.slice(start, stop + 1);
reader.readAsText(blob);
}
// Startup
$(document).ready(function() {
if (! window.File || ! window.FileReader || ! window.Blob) {
setStatus(txt("statusUnsupportedBrowser"));
return;
}
if (activateScanner) {
mw.util.addPortletLink("p-navigation", mw.util.wikiGetlink(scannerPage), txt("generalName"), "EagleEye-portlet", txt("generalStartScanner"));
}
if (typeof rules === "undefined" || rules == null || rules.length == 0) {
setStatus(txt("statusRulesetMissing"));
return;
}
if (mw.config.get("wgPageName") == scannerPage && mw.config.get("wgAction") === "view" && activateScanner) {
// Scanner view
mw.util.addCSS("div.eeSection { border: 1px solid black; padding: 8px; }");
loadScannerUI();
} else if ($.inArray(mw.config.get("wgNamespaceNumber"), namespaces) != -1) {
if (mw.config.get("wgAction") === "view" && activateMarker) {
// Article view
mw.util.addCSS("span.eeMarker { " + markerStyle + " }");
markView();
} else if ($.inArray(mw.config.get("wgAction"), [ "edit", "submit" ]) != -1 && activateScript) {
// Edit view - add button
$("#wpTextbox1").on("wikiEditor-toolbar-doneInitialSections", function () {
mw.loader.using("ext.wikiEditor.toolbar", addButton());
});
}
}
});
})();