//See [[mediawikiwiki:User:Remember the dot/Syntax highlighter]]
//Modified by me (Eighty5cacao) - __NOINDEX__{{DEFAULTSORT:syntax highlighter experimental.js}}
(function () {
"use strict";
//variables that are preserved between function calls
var textboxContainer;
var wpTextbox0;
var wpTextbox1;
var syntaxStyleTextNode;
var lastText;
var maxSpanNumber = -1; //number of the last span available, used to tell if creating additional spans is necessary
var highlightSyntaxIfNeededIntervalID;
//I made up the "zz" convention for no good reason independently of R.t.d's official fix
var zzUserLanguage = mw.config.get( 'wgUserLanguage' );
var zzAction = mw.config.get( 'wgAction' );
var zzPageContentModel = mw.config.get( 'wgPageContentModel' );
/* Define context-specific regexes, one for every common token that ends the
current context.
An attempt has been made to search for the most common syntaxes first,
thus maximizing performance. Syntaxes that begin with the same character
are searched for at the same time. See Remember the dot's original script
for the list.
Only entities for characters which need to be escaped or cannot be
unambiguously represented in a monospace font are highlighted, such as
Greek letters that strongly resemble Latin letters. Use of other entities
is discouraged as a matter of style. For the same reasons, numeric
entities should be in hexadecimal (giving character codes in decimal only
adds confusion).
*/
var breakerRegexBase = "\\[(?:\\[|(?:https?:|ftp:)?//|mailto:)|\\{(?:\\{\\{?|\\|)|<(?:[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:\\w\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD-\\.\u00B7\u0300-\u036F\u203F-\u203F-\u2040]*(?=/?>| )|!--[^]*?-->)|(?:https?://|ftp://|mailto:)[^\\s\"<>[\\]{-}]*[^\\s\",\\.:;<>[\\]{-}]|^(?:=|[*#:;]+|-{4,})|\\\\'\\\\'(?:\\\\')?|&(?:(?:n(?:bsp|dash)|m(?:dash|inus)|lt|e[mn]sp|thinsp|amp|quot|gt|shy|zwn?j|lrm|rlm|Alpha|Beta|Epsilon|Zeta|Eta|Iota|Kappa|[Mm]u|micro|Nu|[Oo]micron|[Rr]ho|Tau|Upsilon|Chi)|#x[0-9a-fA-F]+);|~{3,5}";
function breakerRegexWithPrefix(prefix)
{
//stop token has to be at the beginning of the regex so that it takes precedence over substrings of itself.
//suck up newlines into the end token to avoid creating spans with nothing but newlines in them
return new RegExp("(" + prefix + ")\n*|" + breakerRegexBase, "gm");
}
var defaultBreakerRegex = new RegExp(breakerRegexBase, "gm");
var wikilinkBreakerRegex = breakerRegexWithPrefix("]][a-zA-Z]*");
var namedExternalLinkBreakerRegex = breakerRegexWithPrefix("]");
var parameterBreakerRegex = breakerRegexWithPrefix("}}}");
var templateBreakerRegex = breakerRegexWithPrefix("}}");
var tableBreakerRegex = breakerRegexWithPrefix("\\|}");
var headingBreakerRegex = breakerRegexWithPrefix("\n");
var tagBreakerRegexCache = {};
//browser workaround triggers
var trident = ($.client.profile().layout == "trident");
function highlightSyntax()
{
/* Accommodate the Firefox configuration setting
"privacy.resistFingerprinting.reduceTimerPrecision.microseconds"
which is enabled by the related setting "privacy.reduceTimerPrecision"
(these should hopefully be self-explanatory).
The quantization interval used in Tor Browser is 100ms.
(XXX: This isn't relevant to Wikimedia Foundation wikis for obvious reasons.)
In Firefox 57 and earlier, the timer quantization behavior was triggered
by the "privacy.resistFingerprinting" setting and fixed to 100ms.
(XXX: Documentation elsewhere in this script, including the variable name
itself, may still refer to the old setting.)
The current hackish workaround is to exponentially smooth
the measured intervals with a randomized weight.
Perhaps we should instead switch in an alternative algorithm
that simply averages the duration of all attempts.
XXX: This might not need to be global, but I check it
from other user scripts. */
window.syntaxHlDetectedResistFingerprintingPref = true;
lastText = wpTextbox1.value;
/* Backslashes and apostrophes are CSS-escaped at the beginning and all
parsing regexes and functions are designed to match. On the other hand,
newlines are not escaped until written so that in the regexes ^ and $
work for both newlines and the beginning or end of the string. */
var text = lastText.replace(/['\\]/g, "\\$&") + "\n"; //add a newline to fix scrolling and parsing issues
var i = 0; //location of the parser as it goes through var text
var css = "";
var spanNumber = 0;
var lastColor;
var before = true;
/* Highlighting bold or italic markup presents a special challenge
because the actual MediaWiki parser uses multiple passes to determine
which ticks represent start tags and which represent end tags.
Because that would be too slow for us here, we instead keep track of
what kinds of unclosed opening ticks have been encountered and use
that to make a good guess as to whether the next ticks encountered
are an opening tag or a closing tag.
The major downsides to this method are that '''apostrophe italic''
and ''italic apostrophe''' are not highlighted correctly, and bold
and italic are both highlighted in the same color. */
var assumedBold = false;
var assumedItalic = false;
//writes text into to-be-created span elements of wpTextbox0 using :before and :after pseudo-elements
//both :before and :after are used because using two pseudo-elements per span is significantly faster than doubling the number of spans required
function writeText(text, color)
{
//no need to use another span if using the same color
if (color != lastColor)
{
//whitespace is omitted in the hope of increasing performance
css += "'}#s" + spanNumber; //spans will be created with IDs s0 through sN
if (before)
{
css += ":before{";
before = false;
}
else
{
css += ":after{";
before = true;
++spanNumber;
}
if (color)
{
//"background-color" is 6 characters longer than "background" but the browser processes it faster
css += "background-color:" + color + ";";
}
css += "content:'";
lastColor = color;
}
css += text;
}
function highlightBlock(color, breakerRegex)
{
var match;
for (breakerRegex.lastIndex = i; match = breakerRegex.exec(text); breakerRegex.lastIndex = i)
{
if (match[1])
{
//end token found
writeText(text.substring(i, breakerRegex.lastIndex), color);
i = breakerRegex.lastIndex;
return;
}
var endIndexOfLastColor = breakerRegex.lastIndex - match[0].length;
if (i < endIndexOfLastColor) //avoid calling writeText with text == "" to improve performance
{
writeText(text.substring(i, endIndexOfLastColor), color);
}
i = breakerRegex.lastIndex;
switch (match[0].charAt(0)) //cases in this switch should be arranged from most common to least common
{
case "[":
if (match[0].charAt(1) == "[")
{
//wikilink
writeText("[[", syntaxHighlighterConfig.wikilinkColor || color);
highlightBlock(syntaxHighlighterConfig.wikilinkColor || color, wikilinkBreakerRegex);
}
else
{
//named external link
writeText(match[0], syntaxHighlighterConfig.externalLinkColor || color);
highlightBlock(syntaxHighlighterConfig.externalLinkColor || color, namedExternalLinkBreakerRegex);
}
break;
case "{":
if (match[0].charAt(1) == "{")
{
if (match[0].length == 3)
{
//parameter
writeText("{{{", syntaxHighlighterConfig.parameterColor || color);
highlightBlock(syntaxHighlighterConfig.parameterColor || color, parameterBreakerRegex);
}
else
{
//template
writeText("{{", syntaxHighlighterConfig.templateColor || color);
highlightBlock(syntaxHighlighterConfig.templateColor || color, templateBreakerRegex);
}
}
else //|
{
//table
writeText("{|", syntaxHighlighterConfig.tableColor || color);
highlightBlock(syntaxHighlighterConfig.tableColor || color, tableBreakerRegex);
}
break;
case "<":
if (match[0].charAt(1) == "!")
{
//comment tag
writeText(match[0], syntaxHighlighterConfig.commentColor || color);
break;
}
else
{
//some other kind of tag, search for its end
//search is made easier because XML attributes may not contain the character ">"
var tagEnd = text.indexOf(">", i) + 1;
if (tagEnd == 0)
{
//not a tag, just a "<" with some text after it
writeText("<", color);
i = i - match[0].length + 1;
break;
}
if (text.charAt(tagEnd - 2) == "/")
{
//empty tag
writeText(text.substring(i - match[0].length, tagEnd), syntaxHighlighterConfig.tagColor || color);
i = tagEnd;
}
else
{
var tagName = match[0].substring(1);
//again, cases are ordered from most common to least common
//TODO: provide option to disable hiero (and others) for non-WMF wikis that lack the extension
if (/^(?:nowiki|pre|math|syntaxhighlight|source|timeline|hiero)$/.test(tagName))
{
//tag that can contain only plain text
var stopAfter = "</" + tagName + ">";
var endIndex = text.indexOf(stopAfter, i);
if (endIndex == -1)
{
endIndex = text.length;
}
else
{
endIndex += stopAfter.length;
}
writeText(text.substring(i - match[0].length, endIndex), syntaxHighlighterConfig.tagColor || color);
i = endIndex;
}
else
{
//ordinary tag
writeText(text.substring(i - match[0].length, tagEnd), syntaxHighlighterConfig.tagColor || color);
i = tagEnd;
if (!tagBreakerRegexCache[tagName])
{
tagBreakerRegexCache[tagName] = breakerRegexWithPrefix("</" + tagName + ">");
}
highlightBlock(syntaxHighlighterConfig.tagColor || color, tagBreakerRegexCache[tagName]);
}
}
}
break;
case "h":
case "f":
case "m":
//bare external link
writeText(match[0], syntaxHighlighterConfig.externalLinkColor || color);
break;
case "=":
if (/[^=]=+$/.test(text.substring(i, text.indexOf("\n", i)))) //line begins and ends with an equals sign and has something else in the middle
{
//heading
writeText("=", syntaxHighlighterConfig.headingColor || color);
highlightBlock(syntaxHighlighterConfig.headingColor || color, headingBreakerRegex);
}
else
{
writeText("=", color); //move on, process this line as regular wikitext
}
break;
case "*":
case "#":
case ":":
//unordered list, ordered list, indent, small heading
//just highlight the marker
writeText(match[0], syntaxHighlighterConfig.listOrIndentColor || color);
break;
case ";":
//small heading
writeText(";", syntaxHighlighterConfig.headingColor || color);
highlightBlock(syntaxHighlighterConfig.headingColor || color, headingBreakerRegex);
break;
case "-":
//horizontal line
writeText(match[0], syntaxHighlighterConfig.hrColor || color);
break;
case "\\":
writeText(match[0], syntaxHighlighterConfig.boldOrItalicColor || color);
if (match[0].length == 6)
{
//bold
if (assumedBold)
{
//end tag
assumedBold = false;
return;
}
else
{
//start tag
assumedBold = true;
highlightBlock(syntaxHighlighterConfig.boldOrItalicColor || color, defaultBreakerRegex);
}
}
else
{
//italic
if (assumedItalic)
{
//end tag
assumedItalic = false;
return;
}
else
{
//start tag
assumedItalic = true;
highlightBlock(syntaxHighlighterConfig.boldOrItalicColor || color, defaultBreakerRegex);
}
}
break;
case "&":
//entity
writeText(match[0], syntaxHighlighterConfig.entityColor || color);
break;
case "~":
//username, signature, timestamp
writeText(match[0], syntaxHighlighterConfig.signatureColor || color);
}
}
}
//start!
clearInterval(highlightSyntaxIfNeededIntervalID);
var startTime = Date.now();
window.syntaxHlDetectedResistFingerprintingPref = window.syntaxHlDetectedResistFingerprintingPref &&
( startTime % 100 == 0 );
highlightBlock("", defaultBreakerRegex);
//output the leftovers (if any) to make sure whitespace etc. matches
if (i < text.length)
{
writeText(text.substring(i), "");
}
//do we have enough span elements to match the generated CSS?
while (maxSpanNumber < spanNumber)
{
wpTextbox0.appendChild(document.createElement("span")).id = "s" + ++maxSpanNumber;
}
/* finish CSS: move the extra '} from the beginning to the end and CSS-
escape newlines. CSS ignores the space after the hex code of the
escaped character */
syntaxStyleTextNode.nodeValue = css.substring(2).replace(/\n/g, "\\A ") + "'}";
//if highlighting took too long, disable it.
//first check the time...
var endTime = Date.now();
window.syntaxHlDetectedResistFingerprintingPref = window.syntaxHlDetectedResistFingerprintingPref &&
( endTime % 100 == 0 );
//"real" only means not clamped;
//we may possibly smooth it in response to the resist-fingerprinting pref
var realDelta = Math.max( endTime - startTime, 0 );
if ( window.syntaxHlDetectedResistFingerprintingPref === true ) {
if ( typeof window.syntaxHlPrevSmoothtime === 'number' ) {
//not first sample; perform smoothing
var rndwt = ( Math.random( ) + Math.random( ) ) / 2;
realDelta = realDelta * rndwt + window.syntaxHlPrevSmoothtime * (1-rndwt);
window.syntaxHlPrevSmoothtime = realDelta;
realDelta = Math.round( realDelta );
}
else if ( realDelta === 0 ) {
//first sample is zero, which would ordinarily be unrealistic
window.syntaxHlPrevSmoothtime = 1;
if ( Math.random( ) < 0.5 ) { realDelta = 1; }
}
else {
//first sample is X00ms; use it verbatim
window.syntaxHlPrevSmoothtime = realDelta;
}
}
var clampedDelta = Math.min( syntaxHighlighterConfig.fallbackInterval - 1, realDelta );
if ( syntaxHighlighterConfig.adaptiveTiming )
{
//Adaptive timing strategy: Update the timeout only if the last highlight attempt broke
//our record for best or worst time, in which case we compromise between the old timeout
//and a representative point (harmonic midpoint) in the range.
if ( syntaxHighlighterConfig.spikeTolerance == -1 ) {
//logarithmic mean (previously tried geometric)
var logarithmean =
( syntaxHighlighterConfig.fallbackInterval - syntaxHighlighterConfig.timeout ) /
( Math.log( syntaxHighlighterConfig.fallbackInterval ) - Math.log( syntaxHighlighterConfig.timeout ) );
syntaxHighlighterConfig.spikeTolerance = Math.round( logarithmean );
}
if ( typeof window.syntaxHlTimeoutUpdatesLeft !== 'number' ) {
window.syntaxHlTimeoutUpdatesLeft = 3;
if ( !window.syntaxHlDetectedResistFingerprintingPref ) { window.syntaxHlTimeoutUpdatesLeft++; }
}
if ( typeof window.syntaxHlBestTime !== 'number' )
{
window.syntaxHlBestTime = clampedDelta;
window.syntaxHlWorstTime = clampedDelta;
//don't update on first step if time was small, so we don't get oversensitive
window.syntaxHlUpdateTimeout =
( clampedDelta >= syntaxHighlighterConfig.timeout * 0.18 ) &&
!window.syntaxHlDetectedResistFingerprintingPref;
}
else
{
if (window.syntaxHlUpdateTimeout && window.syntaxHlTimeoutUpdatesLeft > 0)
{
//harmonic mean of 0 with anything else is 0: undesirable
var tb = window.syntaxHlBestTime + 1;
var tw = window.syntaxHlWorstTime+ 1;
syntaxHighlighterConfig.timeout = Math.ceil( ( syntaxHighlighterConfig.timeout - 1 ) / 2 + (tb*tw)/(tb+tw) );
window.syntaxHlTimeoutUpdatesLeft--;
}
if (clampedDelta < window.syntaxHlBestTime)
{
window.syntaxHlBestTime = clampedDelta;
window.syntaxHlUpdateTimeout = true;
}
else if (clampedDelta > window.syntaxHlWorstTime)
{
window.syntaxHlWorstTime = clampedDelta;
window.syntaxHlUpdateTimeout = true;
}
else { window.syntaxHlUpdateTimeout = false; }
}
}
else
{
//non-adaptive: don't tolerate spikes by default
if ( syntaxHighlighterConfig.spikeTolerance == -1 ) { syntaxHighlighterConfig.spikeTolerance = 0; }
}
if (realDelta > syntaxHighlighterConfig.timeout)
{
//rather than allowing a fixed number of spikes, spikeTolerance is a "time buffer" that decays w/each spike
//spike exceeds tolerance (if any); complain and abort
if (realDelta > syntaxHighlighterConfig.timeout + syntaxHighlighterConfig.spikeTolerance)
{
window.syntaxHlAborted = true;
syntaxStyleTextNode.nodeValue = "";
wpTextbox1.removeEventListener("input", highlightSyntax);
var errorMessage = {};
errorMessage["ca"] = "S'ha desactivat el remarcar de sintaxi en aquesta pàgina perquè ha tardat massa. El temps màxim permès per a remarcar és $1ms, i el teu navegador web ha trigat $2ms. Prova tancar algunes pestanyes i programes i fer clic en \"Mostra la previsualització\" o \"Mostra els canvis\". Si no funciona això, prova altre navegador web o un ordinador més ràpid.";
errorMessage["de"] = "Die Syntaxhervorhebung wurde auf dieser Seite deaktiviert, da diese zu lange gedauert hat. Die maximal erlaubte Zeit zur Hervorhebung beträgt $1ms und dein Computer benötigte $2ms. Versuche einige Tabs und Programme zu schließen und klicke \"Vorschau zeigen\" oder \"Änderungen zeigen\". Wenn das nicht probiere einen anderen Webbrowser und wenn immer noch nicht, probiere einen schnelleren Computer.";
errorMessage["en"] = "Syntax highlighting on this page was disabled because it took too long. The maximum allowed highlighting time is $1ms, and your browser took $2ms. If you are editing an entire article, try editing a section instead. If you don't want to cancel this edit, try closing some tabs and programs and clicking \"Show preview\" or \"Show changes\". If this recurs, try a different web browser or a faster computer. (Mobile devices are not recommended.)"; //MODIFIED - translations not updated!
errorMessage["es"] = "Se desactivó el resaltar de sintaxis en esta página porque tardó demasiado. El tiempo máximum permitido para resaltar es $1ms, y tu navegador web tardó $2ms. Prueba cerrar algunas pestañas y programas y hacer clic en \"Mostrar previsualización\" o \"Mostrar cambios\". Si no funciona esto, prueba otro navegador web o un ordenador más rápido. (No se recomiendan los dispositivos móviles.)"; //updated only partially
errorMessage["pt"] = "O marcador de sintaxe foi desativado nesta pagina porque demorou demais. O tempo máximo permitido para marcar e $1ms, e seu navegador web demorou $2ms. Tenta sair de alguns programas e clique em \"Mostrar previsão\" ou \"Mostrar alterações\". Se isso não funciona, tenta usar uma outra navegador web ou um computador mais rápido.";
if ( typeof zzUserLanguage !== 'string' ) zzUserLanguage = 'en';
syntaxHighlighterConfig.languageOverride = syntaxHighlighterConfig.languageOverride || zzUserLanguage;
if ( typeof syntaxHighlighterConfig.timeoutMsg === 'string' ) {
//honor user setting even if it is an empty string, which is falsy
//thus we need to use typeof instead of a boolean test like below
errorMessage = syntaxHighlighterConfig.timeoutMsg;
}
else {
errorMessage = errorMessage[syntaxHighlighterConfig.languageOverride] ||
errorMessage[syntaxHighlighterConfig.languageOverride.substring(0, syntaxHighlighterConfig.languageOverride.indexOf("-"))] ||
errorMessage["en"];
}
wpTextbox1.style.backgroundColor = "";
wpTextbox1.style.position = "";
wpTextbox0.style.color = "red";
wpTextbox0.style.fontFamily = "";
wpTextbox0.style.fontWeight = "bold";
wpTextbox0.style.height = "";
wpTextbox0.style.overflowY = "auto";
var notifyDeltaStr = realDelta + '';
if ( window.syntaxHlDetectedResistFingerprintingPref === true ) {
notifyDeltaStr = '≈' + notifyDeltaStr;
}
var range = document.createRange();
range.selectNode(wpTextbox0); //chrome can't live without this
wpTextbox0.appendChild(range.createContextualFragment(errorMessage.replace("$1", syntaxHighlighterConfig.timeout).replace("$2", notifyDeltaStr)));
if ( typeof syntaxHighlighterConfig.timeoutCallback == 'function' ) syntaxHighlighterConfig.timeoutCallback( syntaxHighlighterConfig.timeout, realDelta );
}
else {
//tolerating a spike
var overage = realDelta - syntaxHighlighterConfig.timeout;
syntaxHighlighterConfig.spikeTolerance = Math.min(
syntaxHighlighterConfig.spikeTolerance - overage,
Math.floor( syntaxHighlighterConfig.spikeTolerance * Math.sqrt( Math.random( ) * Math.random( ) ) )
);
syntaxHighlighterConfig.spikeTolerance = Math.max( syntaxHighlighterConfig.spikeTolerance, 0 );
window.syntaxHlNumSpikesTolerated++;
highlightSyntaxIfNeededIntervalID = setInterval(highlightSyntaxIfNeeded, syntaxHighlighterConfig.fallbackInterval);
}
}
else {
//success w/o spike
highlightSyntaxIfNeededIntervalID = setInterval(highlightSyntaxIfNeeded, syntaxHighlighterConfig.fallbackInterval);
}
}
function syncScrollX()
{
wpTextbox0.scrollLeft = wpTextbox1.scrollLeft;
}
function syncScrollY()
{
wpTextbox0.scrollTop = wpTextbox1.scrollTop;
}
//this function runs once every 800ms by default (changed from 500ms) to detect changes to wpTextbox1's text that the input event does not catch
//this happens when another script changes the text without knowing that the syntax highlighter needs to be informed
//(XXX: document how other scripts *should* inform this one, if it really matters that much)
function highlightSyntaxIfNeeded()
{
if (wpTextbox1.value != lastText)
{
highlightSyntax();
}
if (wpTextbox1.scrollLeft != wpTextbox0.scrollLeft)
{
syncScrollX();
}
if (wpTextbox1.scrollTop != wpTextbox0.scrollTop)
{
syncScrollY();
}
}
function setup()
{
function configureColor(parameterName, hardcodedFallback)
{
if (syntaxHighlighterConfig[parameterName] === "normal")
{
syntaxHighlighterConfig[parameterName] = hardcodedFallback;
}
else if (syntaxHighlighterConfig[parameterName] !== undefined)
{
return;
}
else if (syntaxHighlighterConfig.defaultColor !== undefined)
{
syntaxHighlighterConfig[parameterName] = syntaxHighlighterConfig.defaultColor;
}
else
{
syntaxHighlighterConfig[parameterName] = hardcodedFallback;
}
}
window.syntaxHighlighterConfig = window.syntaxHighlighterConfig || {};
if ( typeof window.syntaxHlAborted !== 'boolean' ) window.syntaxHlAborted = false;
//use 3-digit colors instead of 6-digit colors for performance
configureColor("boldOrItalicColor", "#EEE"); //gray
configureColor("commentColor", "#EFE"); //green
configureColor("entityColor", "#DFD"); //green
configureColor("externalLinkColor", "#EFF"); //cyan
configureColor("headingColor", "#EEE"); //gray
configureColor("hrColor", "#EEE"); //gray
configureColor("listOrIndentColor", "#EFE"); //green
configureColor("parameterColor", "#FC6"); //orange
configureColor("signatureColor", "#FC6"); //orange
configureColor("tagColor", "#FEF"); //pink
configureColor("tableColor", "#FFC"); //yellow
configureColor("templateColor", "#FFC"); //yellow
configureColor("wikilinkColor", "#EEF"); //blue
syntaxHighlighterConfig.fallbackInterval = syntaxHighlighterConfig.fallbackInterval || 800;
//The default needs to be lenient enough to accommodate "privacy.resistFingerprinting" and related Firefox settings,
//as mentioned above. We do not know whether the user has enabled these until we start collecting time samples.
syntaxHighlighterConfig.timeout = syntaxHighlighterConfig.timeout || (
syntaxHighlighterConfig.adaptiveTiming ? 141 + Math.floor( 110 * ( Math.random( ) + Math.random( ) ) / 2 ) : 101
);
syntaxHighlighterConfig.timeout = Math.min( syntaxHighlighterConfig.fallbackInterval - 1, syntaxHighlighterConfig.timeout );
if ( typeof syntaxHighlighterConfig.spikeTolerance !== 'number' )
syntaxHighlighterConfig.spikeTolerance = -1; //-1 signals above code to set default
window.syntaxHlNumSpikesTolerated = 0;
textboxContainer = document.createElement("div");
wpTextbox0 = document.createElement("div");
wpTextbox1 = document.getElementById("wpTextbox1");
var syntaxStyleElement = document.createElement("style");
syntaxStyleElement.type = "text/css";
syntaxStyleTextNode = syntaxStyleElement.appendChild(document.createTextNode(""));
//styling of the textbox and the background div must be kept very similar
var wpTextbox1Style = window.getComputedStyle(wpTextbox1);
var scrollTop = wpTextbox1.scrollTop;
var focus = (document.activeElement == wpTextbox1);
wpTextbox0.style.backgroundColor = wpTextbox1Style.backgroundColor;
wpTextbox0.style.border = "1px solid transparent";
wpTextbox0.style.boxSizing = "border-box";
wpTextbox0.style.color = "transparent"; //makes it look just a little bit smoother
wpTextbox0.style.direction = wpTextbox1Style.direction; //because your UI language set in Preferences may have a different direction than the edit box
wpTextbox0.style.fontFamily = wpTextbox1Style.fontFamily;
wpTextbox0.style.fontSize = wpTextbox1Style.fontSize;
wpTextbox0.style.lineHeight = "normal";
wpTextbox0.style.overflowX = "auto";
wpTextbox0.style.overflowY = "scroll";
wpTextbox0.style.whiteSpace = "pre-wrap";
wpTextbox0.style.width = "100%";
wpTextbox0.style.wordWrap = "normal"; //see below
wpTextbox1.style.backgroundColor = "transparent";
wpTextbox1.style.border = "1px inset gray";
wpTextbox1.style.boxSizing = "border-box";
wpTextbox1.style.lineHeight = "normal";
wpTextbox1.style.margin = "0"; //firefox wants to put a 1px margin on the top and bottom of the textbox, which throws it out of alignment with wpTextbox0
wpTextbox1.style.overflowX = "auto";
wpTextbox1.style.overflowY = "scroll";
wpTextbox1.style.padding = "0";
wpTextbox1.style.position = "absolute";
wpTextbox1.style.resize = "none";
wpTextbox1.style.left = "0";
wpTextbox1.style.top = "0";
wpTextbox1.style.width = "100%";
wpTextbox1.style.wordWrap = "normal"; //overall more visually appealing
//lock both heights to pixel values so that the browser zoom feature works better
wpTextbox0.style.height = wpTextbox1.offsetHeight + "px";
wpTextbox1.style.height = wpTextbox0.style.height;
textboxContainer.style.position = "relative";
wpTextbox1.parentNode.insertBefore(textboxContainer, wpTextbox1);
textboxContainer.appendChild(wpTextbox1);
textboxContainer.appendChild(wpTextbox0);
//changing the parent resets scrollTop to 0 and removes focus, so we have to bring them back
wpTextbox0.scrollTop = scrollTop;
wpTextbox1.scrollTop = scrollTop;
if (focus) wpTextbox1.focus();
//fix drop-downs in editing toolbar
$('.tool-select *').css({zIndex: 5});
document.head.appendChild(syntaxStyleElement);
wpTextbox1.addEventListener("input", highlightSyntax);
wpTextbox1.addEventListener("scroll", syncScrollX);
wpTextbox1.addEventListener("scroll", syncScrollY);
highlightSyntaxIfNeededIntervalID = setInterval(highlightSyntaxIfNeeded, syntaxHighlighterConfig.fallbackInterval);
highlightSyntax();
}
function queueSetup()
{
setTimeout(setup, 0);
}
//enable the highlighter only when editing wikitext pages
if ( typeof zzPageContentModel !== 'string' ) { //just defensive programming; we don't actively support old MW versions that lack wgPageContentModel
zzPageContentModel = 'xyzzy';
var zzNN = mw.config.get( 'wgNamespaceNumber' );
var zzT = mw.config.get( 'wgTitle' );
//assume extension namespaces (such as Module:) are not wikitext, except for talk pages
var MAX_SITE_NAMESPACE = 199; //[[mediawikiwiki:Manual:Using custom namespaces#Creating a custom namespace]]
// constant, but strict mode doesn't currently allow const
if ( !((zzNN == 2 || zzNN == 8) && /\.(?:css|js)$/.test(zzT)) && ( zzNN <= MAX_SITE_NAMESPACE || (zzNN % 2) == 1 ) ) zzPageContentModel = 'wikitext';
}
//blacklist Internet Explorer; it's just too broken
if ((zzAction == "edit" || zzAction == "submit") && zzPageContentModel === "wikitext" && !trident)
{
/* The highlighter has to run after any other script (such as the
editing toolbar) that reparents wpTextbox1. We make sure that
everything else has run by waiting for the page to completely load
and then adding a call to the setup function to the end of the event
queue, so that the setup function runs after any other triggers set
on the load event.
XXX: Do current versions of jQuery handle this as intended?
We may need a setTimeout hack if not.
*/
$( queueSetup );
}
})();
Categories: Noindexed pages