Benutzer:Schnark/js/syntaxhighlight.js
Erscheinungsbild
< Benutzer:Schnark | 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
- Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
//based on [[mw:User:Remember the dot/Syntax highlighter.js]] with a completely different parser
//Dokumentation unter [[Benutzer:Schnark/js/syntaxhighlight]]
//<nowiki>
/*global mediaWiki*/
(function($, mw, libs) {
"use strict";
var map, protocols,
wikiSyntax, cssSyntax, jsSyntax, luaSyntax,
defaultWikitextColors, defaultCssColors, defaultJsColors, strongColors;
map = {
opera: [['>=', 15]],
msie: false
};
function clone (array) {
return [Array.prototype.slice.call(array[0]), Array.prototype.slice.call(array[1])];
}
function Parser (syntax) {
var re, res = [];
this.syntax = {};
this.syntax.noparse = syntax.noparse || {};
this.syntax.fn = [];
for (re in syntax.parse) {
res.push('(' + re + ')');
this.syntax.fn.push(syntax.parse[re]);
}
this.syntax.re = new RegExp(res.join('|'), 'g');
this.syntax.eol = syntax.eol || function (o) {
return o;
};
this.syntax.parens = syntax.parens || {
open: '([{',
close: ')]}'
};
this.syntax.parens.all = this.syntax.parens.open + this.syntax.parens.close;
this.cache = {};
}
Parser.prototype = {
open: function (type) {
this.openTags.push(type);
},
close: function (type) {
if (this.openTags.length === 0) {
return;
}
if (this.openTags[this.openTags.length - 1] === type) {
this.openTags.pop();
return;
}
var i = this.openTags.lastIndexOf(type);
if (i > -1) {
this.openTags.length = i;
}
},
isOpen: function (type) {
if (this.openTags.length === 0) {
return false;
}
if (this.openTags[this.openTags.length - 1] === type) {
return true;
}
return this.openTags.lastIndexOf(type) > -1;
},
current: function () {
return this.openTags[this.openTags.length - 1] || '';
},
noparse: function () {
return this.syntax.noparse[this.current()] || false;
},
updateNoparse: function (type, re) {
this.syntax.noparse[type] = re;
},
exec: function (re) {
re.lastIndex = this.pos;
return re.exec(this.text);
},
getText: function (l) {
return this.text.substring(this.pos, this.pos + l);
},
write: function (text) {
if (text === '') {
return;
}
this.pos += text.length;
this.output.push([text, this.current()]);
},
parse: function (text) {
if (text === this.oldText) {
return this.oldParse;
}
this.oldText = text;
this.oldParse = [];
var i, open = [], ret, par = text.split('\n');
for (i = 0; i < par.length; i++) {
ret = this.parseParagraph(par[i], open);
this.oldParse = this.oldParse.concat(ret[0]);
open = this.syntax.eol(ret[1]);
}
return this.oldParse;
},
parseParagraph: function (par, open) {
if (par === '') {
return [
[['\n', open[open.length - 1] || '']],
open
];
}
var key = open.join('|');
if (!this.cache[key]) {
this.cache[key] = {};
}
if (!this.cache[key][par + '\n']) {
this.cache[key][par + '\n'] = this.reallyParseParagraph(par, open);
}
return clone(this.cache[key][par + '\n']);
},
reallyParseParagraph: function (par, open) {
var noparse, result, i, word;
this.output = [];
this.openTags = open;
this.text = par;
this.pos = 0;
while (this.pos < this.text.length) {
noparse = this.noparse();
if (noparse) {
result = this.exec(noparse);
if (result) {
this.write(this.getText(result.index + result[0].length - this.pos));
this.close(this.current());
} else {
this.write(this.text.substring(this.pos));
}
} else {
result = this.exec(this.syntax.re);
if (result) {
this.write(this.getText(result.index - this.pos));
for (i = 0; i < this.syntax.fn.length; i++) {
if (result[i + 1]) {
word = result[i + 1];
this.syntax.fn[i].call(this, word, this.text.substring(this.pos + word.length), this.text.substring(0, this.pos));
break;
}
}
} else {
this.write(this.text.substring(this.pos));
}
}
}
this.write('\n');
return clone([this.output, this.openTags]);
},
findMatchingParen: function (text, pos) {
var me = text.charAt(pos - 1), other, myPos = pos - 1, dir, depth = 1;
if (me === '' || this.syntax.parens.all.indexOf(me) === -1) {
me = text.charAt(pos);
myPos = pos;
}
if (me === '' || this.syntax.parens.all.indexOf(me) === -1) {
return false;
}
dir = this.syntax.parens.open.indexOf(me) === -1 ? -1 : 1;
other = this.syntax.parens[dir === 1 ? 'close' : 'open'].charAt(this.syntax.parens[dir === 1 ? 'open' : 'close'].indexOf(me));
pos = myPos;
do {
pos += dir;
if (text.charAt(pos) === me) {
depth++;
} else if (text.charAt(pos) === other) {
depth--;
if (depth === 0) {
return [Math.min(pos, myPos), Math.max(pos, myPos)];
}
}
} while (text.charAt(pos) !== '');
return false;
},
parseWithParen: function (text, pos) {
var output = this.parse(text), parens = this.findMatchingParen(text, pos), newOutput = [], i, j = 0, oldLen = 0, newLen, t;
if (!parens) {
return output;
}
parens.push(Infinity);
for (i = 0; i < output.length; i++) {
t = output[i][0];
newLen = oldLen + t.length;
while (parens[j] < newLen) {
if (parens[j] > oldLen) {
newOutput.push([t.substring(0, parens[j] - oldLen), output[i][1]]);
t = t.substring(parens[j] - oldLen);
}
newOutput.push([t.charAt(0), 'matching-paren']);
t = t.substring(1);
oldLen = parens[j] + 1;
j++;
}
if (t) {
newOutput.push([t, output[i][1]]);
}
oldLen = newLen;
}
return newOutput;
}
};
function makeParserFunctionOpen (type) {
return function (text) {
this.open(type);
this.write(text);
};
}
function makeParserFunctionType (type) {
return function (text) {
this.open(type);
this.write(text);
this.close(type);
};
}
function makeParserFunctionClose (type) {
return function (text) {
this.write(text);
this.close(type);
};
}
protocols = new RegExp('^' + mw.config.get('wgUrlProtocols'));
function parsePlainLink (proto, text) {
/*jshint validthis: true*///Parser-Funktion mit explizitem Kontext
var link = /[^ <>|\[\]]*/.exec(text)[0], punc = ',;.:!?';
if (link.indexOf('(') === -1) {
punc += ')';
}
while (link && punc.indexOf(link.charAt(link.length - 1)) !== -1) {
link = link.substring(0, link.length - 1);
}
this.open('externalLink');
this.write(proto + link);
this.close('externalLink');
}
function keywords (words, type) {
var i, syntax = {};
for (i = 0; i < words.length; i++) {
syntax['\\b' + words[i] + '\\b'] = makeParserFunctionType(type || 'keyword');
}
return syntax;
}
wikiSyntax = {
noparse: {
'comment': /-->/g,
'<hiero>': /<\/hiero>/gi,
'<math>': /<\/math>/gi,
'<nowiki>': /<\/nowiki>/gi,
'<pre>': /<\/pre>/gi,
'<score>': /<\/score>/gi,
'<source>': /<\/source>/gi,
'<syntaxhighlight>': /<\/syntaxhighlight>/gi,
'<templatedata>': /<\/templatedata>/gi,
'<timeline>': /<\/timeline>/gi
},
parse: {
'^=': makeParserFunctionOpen('heading'),
'^ ': function () {
if (!this.isOpen('template')) {
this.open('pre');
}
this.write(' ');
},
'^[*#:;]+': makeParserFunctionType('listAndIndent'),
'^-{4,}': makeParserFunctionType('hr'),
'\\[\\[': makeParserFunctionOpen('wikilink'),
'\\[': function (x, text) {
if (protocols.test(text)) {
this.close('externalLink');
this.open('externalLink');
}
this.write('[');
},
'\\]': function (x, text) {
if (this.isOpen('externalLink')) {
this.write(']');
this.close('externalLink');
} else if (text.charAt(0) === ']') {
this.write(']]');
this.close('wikilink');
} else {
this.write(']');
}
},
'<!--': makeParserFunctionOpen('comment'),
'</?[a-zA-Z]+[^>]*>': function (tag) {
var tagParts = (/<(\/?)([a-z]+)([^>]*)>/i).exec(tag),
tagname = tagParts[2].toLowerCase(),
selfclosing = tagParts[3] && tagParts[3].charAt(tagParts[3].length - 1) === '/',
singleTag = ['br', 'hr', 'wbr', 'nowiki', 'ref', 'references', 'section'],
selfclosingTag = ['br', 'hr', 'wbr'];
if (tagParts[1]) {
this.write(tag);
this.close('<' + tagname + '>');
} else if ((selfclosing && $.inArray(tagname, singleTag) > -1) || $.inArray(tagname, selfclosingTag) > -1) {
this.open('<' + tagname + '>');
this.write(tag);
this.close('<' + tagname + '>');
} else {
this.open('<' + tagname + '>');
this.write(tag);
}
},
'\\{\\{\\{': makeParserFunctionOpen('parameter'),
'\\{\\{': makeParserFunctionOpen('template'),
'\\{\\|': makeParserFunctionOpen('table'),
'\\}': function (x, text, before) {
var closeTable = (before === '|'), count = 1, i;
if (text.charAt(0) === '}') {
count = 2;
if (text.charAt(1) === '}') {
count = 3;
}
}
if (count === 1) {
this.write('}');
if (closeTable) {
this.close('table');
}
return;
}
if (count === 3 && this.current() !== 'template' && this.isOpen('parameter')) {
this.write('}}}');
this.close('parameter');
return;
}
if (!closeTable || !this.isOpen('table')) {
this.write('}}');
this.close('template');
return;
}
if (!this.isOpen('template')) {
this.write('}');
this.close('table');
return;
}
//both an open table and a template, and a string "|}}"
for (i = this.openTags.length - 1; i--; i >= 0) {
if (this.openTags[i] === 'table') {
this.write('}');
this.close('table');
return;
} else if (this.openTags[i] === 'template') {
this.write('}}');
this.close('template');
return;
}
}
},
'~{3,5}': makeParserFunctionType('signature'),
'https?:\/\/': parsePlainLink,
"'''''": function () { //FIXME both '''''bi''b''' and '''''bi'''i'' are ok
var b = this.isOpen('bold'), i = this.isOpen('italic');
if (b && i) {
this.write("'''''");
this.close('bold');
this.close('italic');
} else if (!b && !i) {
this.open('bold');
this.open('italic');
this.write("'''''");
} else if (b /* && !i */) {
this.write("'''");
this.close('bold');
this.open('italic');
this.write("''");
} else /* if (!b && i) */ {
this.write("''");
this.close('italic');
this.open('bold');
this.write("'''");
}
},
"'''": function () {
if (this.isOpen('bold')) {
this.write("'''");
this.close('bold');
} else {
this.open('bold');
this.write("'''");
}
},
"''": function () {
if (this.isOpen('italic')) {
this.write("''");
this.close('italic');
} else {
this.open('italic');
this.write("''");
}
},
'&(?:#[xX]?\\d+|\\w+);': makeParserFunctionType('entity'),
'__[A-Z_]+__': makeParserFunctionType('magic'),
'\u2013': makeParserFunctionType('char-endash'),
'\u2014': makeParserFunctionType('char-emdash'),
'\u2212': makeParserFunctionType('char-minus')
},
eol: function (open) {
var i;
for (i = 0; i < open.length; i++) {
if ($.inArray(open[i], ['externalLink', 'bold', 'italic', 'heading', 'pre']) !== -1) {
open.length = i;
break;
}
}
return open;
}
};
cssSyntax = {
noparse: {
'comment': /\*\//g
},
parse: {
'/\\*': makeParserFunctionOpen('comment'),
'!important': function () {
if (!this.isOpen('decleration')) {
this.write('!important');
return;
}
this.open('important');
this.write('!important');
this.close('important');
},
'#[^ ,.*#:>{\\[~]*': function (id) {
if (this.isOpen('decleration')) {
this.write('#');
return;
}
this.open('id');
this.write(id);
this.close('id');
},
'\\.[^ ,.*#:>{\\[~]*': function (cls) {
if (this.isOpen('decleration')) {
this.write('.');
return;
}
this.open('class');
this.write(cls);
this.close('class');
},
':[^ ,.*#:>{\\[~]*': function (pseudo, text) {
var i = 0;
if (this.isOpen('decleration')) {
if (pseudo === ':') {
while (text.charAt(i++) === ' ') {
pseudo += ' ';
}
} else {
pseudo = ':';
}
this.write(pseudo);
this.open('value');
return;
}
this.open('pseudo');
this.write(pseudo);
this.close('pseudo');
},
'@\\S*': function (at) {
if (this.isOpen('decleration')) {
this.write('@');
return;
}
this.open('at');
this.write(at);
this.close('at');
},
'\\[': function () {
if (this.isOpen('decleration')) {
this.write('[');
return;
}
this.open('attr');
this.write('[');
},
'\\]': function () {
this.write(']');
this.close('attr');
},
'\\{': function () {
this.open('decleration');
this.write('{');
},
'\\}': function () {
this.close('value');
this.write('}');
this.close('decleration');
},
';': function () {
this.close('value');
this.write(';');
},
'url\\([^)]*\\)?': makeParserFunctionType('string'),
'"(?:[^\\\\"]+|\\\\.)*"?': makeParserFunctionType('string'),
"'(?:[^\\\\']+|\\\\.)*'?": makeParserFunctionType('string')
}
};
jsSyntax = {
noparse: {
'comment': /\*\//g
},
parse: $.extend({
'"(?:[^\\\\"]+|\\\\.)*"?': makeParserFunctionType('string'),
"'(?:[^\\\\']+|\\\\.)*'?": makeParserFunctionType('string'),
'//': function (c, text) {
this.open('comment');
this.write(c + text);
this.close('comment');
},
'/\\*': makeParserFunctionOpen('comment'),
'/(?:[^\\/]+|\\\\.)*/?': function (re, after, before) {
if (/[)\]\w]\s*$/.test(before)) { //probably not a regexp
this.write('/');
} else {
this.open('regexp');
this.write(re);
this.close('regexp');
}
},
'\\[': makeParserFunctionOpen('array'),
'\\]': makeParserFunctionClose('array')
},
//reserved words
keywords(['break', 'case', 'catch', 'continue', 'debugger', 'default', 'delete', /*'do',*/ 'else', 'finally', 'for', 'function',
'if', /*'in',*/ 'instanceof', 'new', 'return', 'switch', 'this', 'throw', 'try', 'typeof', 'var', 'void', 'while'], 'reserved'),
//literals, globals
keywords(['Array', 'Boolean', 'Date', 'Error', 'eval', 'false', 'Function', 'Infinity', 'isFinite', 'isNaN', 'JSON', 'Math',
'NaN', 'null', 'Number', 'Object', 'parseInt', 'parseFloat', 'RegExp', 'String', 'true', 'undefined'], 'global'),
//common functions etc. (incomplete)
keywords(['addEventListener', 'appendChild', 'charAt', 'charCodeAt', 'clearInterval', 'clearTimeout', 'console', 'createElement',
'decodeURIComponent', 'decodeURI', 'document', 'encodeURIComponent', 'encodeURI', 'getElementById', 'getElementsByTagName',
'indexOf', 'insertBefore', 'join', 'length', 'parentNode', 'removeEventListener', 'setInterval', 'setTimeout', 'splice',
'substr', 'substring', 'test', 'window'], 'common'),
//reserved for future / deprecated
keywords(['class', 'const', 'enum', 'export', 'extends', 'implements', 'import', 'interface', 'let', 'package', 'private',
'protected', 'public', 'static', 'super', 'with', 'yield'], 'future'),
//short words
keywords(['do', 'in'], 'reserved')
)
};
luaSyntax = {
parse: $.extend({
'--\\[=*\\[': function (c) {
this.open('comment');
this.write(c);
this.updateNoparse('comment', new RegExp(c.replace(/--/, '').replace(/\[/g, '\\]'), 'g'));
},
'--': function (c, text) {
this.open('comment');
this.write(c + text);
this.close('comment');
},
'"(?:[^\\\\"]+|\\\\.)*"?': makeParserFunctionType('string'),
"'(?:[^\\\\']+|\\\\.)*'?": makeParserFunctionType('string'),
'\\[=*\\[': function (s) {
this.open('string');
this.write(s);
this.updateNoparse('string', new RegExp(s.replace(/\[/g, '\\]'), 'g'));
},
'\\[': makeParserFunctionOpen('array'), //index
'\\]': makeParserFunctionClose('array'),
'\\{': makeParserFunctionOpen('array'), //table
'\\}': makeParserFunctionClose('array')
},
keywords(['and', 'break', 'do', 'elseif', 'else', 'end', 'false', 'for', 'function', 'if', 'in', 'local', 'nil', 'not',
'or', 'repeat', 'return', 'then', 'true', 'until', 'while'], 'reserved'),
keywords(['ipairs', 'next', 'pairs', 'select', 'tonumber', 'tostring', 'type', 'unpack', '_VERSION', 'coroutine',
'module', 'require', 'string', 'table', 'math'], 'common')
)
};
function BasicHighlighter (syntax, colors, box, prefix) {
this.parser = new Parser(syntax);
this.colors = colors;
this.box = box;
this.prefix = prefix;
this.stylesheet = document.getElementsByTagName('head')[0].appendChild(document.createElement('style'));
this.spanCount = 0;
}
BasicHighlighter.prototype = {
enable: function () {
this.stylesheet.disabled = false;
},
disable: function () {
this.stylesheet.disabled = true;
},
destroy: function () {
this.stylesheet.parentNode.removeChild(this.stylesheet);
},
makeSpans: function (n) {
for (; this.spanCount < n; this.spanCount++) {
this.box.appendChild(document.createElement('span')).id = this.prefix + this.spanCount;
}
},
getColor: function (type) {
if (type in this.colors) {
return this.colors[type];
}
if (type.charAt(0) === '<') {
return this.colors.tag;
}
},
getCSS: function (syntax) {
var lastColor = false, css = [], spans = -1, color, text, i;
for (i = 0; i < syntax.length; i++) {
color = this.getColor(syntax[i][1]);
text = syntax[i][0].replace(/(\\|")/g, '\\$1').replace(/\n/g, '\\A ');
if (color !== lastColor) {
if (lastColor !== false) {
css.push('"}');
}
spans++;
lastColor = color;
if (color) {
color = 'background-color:' + color + ';';
} else {
color = '';
}
css.push('#' + this.prefix + Math.floor(spans / 2) + (spans % 2 === 0 ? ':before' : ':after') + '{' + color + 'content:"');
}
css.push(text);
}
css.push('"}');
this.makeSpans(Math.floor(spans / 2) + 1);
return css.join('');
},
highlight: function (text, pos) {
var css;
if (pos === undefined) {
if (text === this.lastText) {
return;
}
this.lastText = text;
css = this.getCSS(this.parser.parse(text));
} else {
this.lastText = false;
css = this.getCSS(this.parser.parseWithParen(text, pos));
}
if (css === this.lastCSS) {
return;
}
this.lastCSS = css;
this.stylesheet.textContent = css;
}
};
function getStyles (el, styles) {
var computedStyle = window.getComputedStyle(el, null), ret = {}, i;
for (i = 0; i < styles.length; i++) {
ret[styles[i]] = computedStyle[styles[i]];
}
return ret;
}
function setStyles (el, styles) {
var s;
for (s in styles) {
el.style[s] = styles[s];
}
}
function Highlighter (syntax, colors, textarea, paren) {
var _this = this;
this.textarea = textarea;
this.initBoxes();
this.basicHighlighter = new BasicHighlighter(syntax, colors, this.highlightbox, textarea.id + '-');
this.onoff = $.noop;
this.reportTime = $.noop;
this.paren = paren;
if (paren) {
this.$textarea = $(this.textarea);
mw.loader.using('jquery.textSelection', function () {
_this.getPos = function () {
return _this.$textarea.textSelection('getCaretPosition');
};
});
}
this.enable();
}
Highlighter.prototype = {
isEnabled: function () {
return this.enabled;
},
enable: function () {
if (this.isEnabled()) {
return;
}
this.bindHandlers();
this.basicHighlighter.enable();
this.enabled = true;
this.highlight();
this.syncScroll();
this.onoff(true);
},
disable: function () {
if (!this.isEnabled()) {
return;
}
this.unbindHandlers();
this.basicHighlighter.disable();
this.enabled = false;
this.onoff(false);
},
proxy: function (f) {
var _this = this;
if (!this.proxyCache) {
this.proxyCache = {};
}
if (!(f in this.proxyCache)) {
this.proxyCache[f] = function () {
_this[f].apply(_this);
};
}
return this.proxyCache[f];
},
initBoxes: function () {
var scrolltop, focus, style, commonStyle, bugfixStyle = {}, profile = $.client.profile();
this.container = document.createElement('div');
this.oldStyle = getStyles(this.textarea, ['backgroundColor', /*'borderTopWidth', 'borderTopStyle', 'borderTopColor',
'borderRightWidth', 'borderRightStyle', 'borderRightColor', 'borderBottomWidth', 'borderBottomStyle',
'borderBottomColor', 'borderLeftWidth', 'borderLeftStyle', 'borderLeftColor',*/ 'marginTop', 'marginRight',
'marginBottom', 'marginLeft', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft', 'overflowX', 'overflowY',
'position', 'resize', 'left', 'top', 'width']);
focus = (this.textarea === this.textarea.ownerDocument.activeElement);
scrolltop = this.textarea.scrollTop;
this.highlightbox = document.createElement('div');
//make sure "highlightbox" is behind the transparent "textarea" and looks *exactly* the same
commonStyle = {
//border: '1px inset gray',
margin: 0,
overflowX: 'auto',
overflowY: 'scroll',
padding: 0,
resize: 'none',
whiteSpace: 'pre-wrap',
width: '100%',
MozBoxSizing: 'border-box',
WebkitBoxSizing: 'border-box',
boxSizing: 'border-box'
};
setStyles(this.textarea, $.extend({
backgroundColor: 'transparent',
position: 'absolute',
left: 0,
top: 0
}, commonStyle));
//workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=157846, see omni.ja!/chrome/toolkit/res/forms.css: textarea > .anonymous-div
if (profile.layout === 'gecko' && profile.layoutVersion <= 20140101) { //FIXME since which version is this fixed (is it really fixed?)
if (profile.platform === 'win') {
bugfixStyle.padding = '0px 1px';
} else {
bugfixStyle.padding = '0px 2px';
}
}
style = getStyles(this.textarea, ['direction', 'fontFamily', 'fontSize', 'lineHeight', 'height', 'wordWrap',
'verticalAlign', 'unicodeBidi', 'MozAppearance', 'WebkitAppearance']);
setStyles(this.highlightbox, $.extend({
backgroundColor: this.oldStyle.backgroundColor,
color: 'transparent'
}, style, commonStyle, bugfixStyle));
setStyles(this.container, {
position: 'relative'
});
this.textarea.parentNode.insertBefore(this.container, this.textarea);
this.container.appendChild(this.highlightbox);
this.container.appendChild(this.textarea);
this.textarea.scrollTop = scrolltop;
if (focus) {
this.textarea.focus();
}
},
destroy: function () {
this.disable();
this.basicHighlighter.destroy();
this.container.parentNode.insertBefore(this.textarea, this.container);
this.container.parentNode.removeChild(this.container);
setStyles(this.textarea, this.oldStyle);
},
bindHandlers: function () {
this.textarea.addEventListener('input', this.proxy('onInput'), false);
if (this.paren) {
this.textarea.addEventListener('keyup', this.proxy('onInput'), false);
}
this.textarea.addEventListener('scroll', this.proxy('syncScroll'), false);
this.intervalID1 = window.setInterval(this.proxy('highlight'), 500);
this.intervalID2 = window.setInterval(this.proxy('syncScroll'), 500);
},
unbindHandlers: function () {
this.textarea.removeEventListener('input', this.proxy('onInput'), false);
if (this.paren) {
this.textarea.removeEventListener('keyup', this.proxy('onInput'), false);
}
this.textarea.removeEventListener('scroll', this.proxy('syncScroll'), false);
window.clearInterval(this.intervalID1);
window.clearInterval(this.intervalID2);
},
getPos: $.noop,
syncScroll: function () {
if (this.highlightbox.scrollLeft !== this.textarea.scrollLeft) {
this.highlightbox.scrollLeft = this.textarea.scrollLeft;
}
if (this.highlightbox.scrollTop !== this.textarea.scrollTop) {
this.highlightbox.scrollTop = this.textarea.scrollTop;
}
},
onInput: function () {
window.setTimeout(this.proxy('highlight'), 1);
},
highlight: function () {
var time = $.now();
this.basicHighlighter.highlight(this.textarea.value, this.getPos());
this.reportTime($.now() - time);
}
};
function makeHighlighter (syntax, colors, id, label) {
var textarea = document.getElementById(id), highlighter, $label, $checkbox;
if (!textarea) {
return;
}
highlighter = new Highlighter(syntax, colors, textarea, true);
if (label) {
$label = $('<label>', {
'for': 'syntaxhighlight'
}).text(label);
$checkbox = $('<input id="syntaxhighlight" type="checkbox" checked="checked" />');
highlighter.onoff = function (on) {
$checkbox.prop('checked', on);
};
$checkbox.change(function () {
if ($checkbox.prop('checked')) {
highlighter.enable();
} else {
highlighter.disable();
}
});
/*$checkbox.on('dblclick', function () {
highlighter.destroy();
});*/
$('.editCheckboxes').append('\n').append($checkbox).append('\n').append($label);
}
if (mw.config.get('wgUserName') === 'Schnark') {
highlighter.reportTime = function (ms) {
if (ms) {
window.console.log(ms + ' ms (#' + id + ')');
}
};
}
}
defaultWikitextColors = {
bold: '#E5E5E5', //'gray'
'char-emdash': '#FFE6FF', //'pink'
'char-endash': '#E5E5E5', //'gray'
'char-minus': '#FFFFCC', //'yellow'
comment: '#E6FFE6', //'green'
entity: '#E6FFE6', //'green'
externalLink: '#E6FFFF', //'cyan'
italic: '#E5E5E5', //'gray'
heading: '#E5E5E5', //'gray'
hr: '#E5E5E5', //'gray'
listAndIndent: '#E6FFE6', //'green'
magic: '#E5E5E5', //'gray'
'matching-paren': '#FFCCCC',
parameter: '#FFCC66', //'orange'
pre: '#E5E5E5', //'gray'
signature: '#FFCC66', //'orange'
tag: '#FFE6FF', //'pink'
table: '#FFFFCC', //'yellow'
template: '#FFFFCC', //'yellow'
wikilink: '#E6E6FF' //'blue'
};
defaultCssColors = {
at: '#FFE6FF',
attr: '#FFE6FF',
'class': '#FFE6FF',
comment: '#E6FFE6',
decleration: '#FFFFCC',
id: '#E6E6FF',
important: '#E6E6FF',
'matching-paren': '#FFCCCC',
pseudo: '#FFE6FF',
string: '#FFCC66',
value: '#E5E5E5'
};
defaultJsColors = {
array: '#FFFFCC',
comment: '#E6FFE6',
common: '#E6FFFF',
future: '#FFE6FF',
global: '#E6FFE6',
'matching-paren': '#FFCCCC',
regexp: '#FFFFCC',
reserved: '#E6E6FF',
string: '#FFCC66'
};
strongColors = { //I use a misconfigured screen, I cannot see the above colors on it at all
at: 'pink',
array: 'yellow',
attr: 'pink',
bold: 'gray',
'char-emdash': 'pink',
'char-endash': 'gray',
'char-minus': 'yellow',
'class': 'pink',
comment: 'green',
common: 'cyan',
decleration: 'yellow',
entity: 'green',
externalLink: 'cyan',
future: 'pink',
global: 'green',
id: '#55f',
important: '#55f',
italic: 'gray',
heading: 'gray',
hr: 'gray',
listAndIndent: 'green',
magic: 'gray',
'matching-paren': 'red',
parameter: 'orange',
pre: 'gray',
pseudo: 'pink',
regexp: 'yellow',
reserved: '#55f',
signature: 'orange',
string: 'orange',
tag: 'pink',
table: 'yellow',
template: 'yellow',
value: 'gray',
wikilink: '#55f'
};
function initHighlighter(ext, label, additional) {
var word, id, colors, syntax;
id = mw.config.get('wgCanonicalSpecialPageName') === 'Upload' ? 'wpUploadDescription' : 'wpTextbox1';
colors = getColors(ext);
syntax = getSyntax(ext);
if (additional) {
for (word in additional) {
colors['additional-' + word] = additional[word];
additional[word] = makeParserFunctionType('additional-' + word);
}
syntax = $.extend({}, additional, syntax);
}
makeHighlighter(syntax, colors, id, id === 'wpTextbox1' ? (label || 'enable syntax highlighter') : false);
if (id === 'wpTextbox1') {
makeHighlighter(syntax, colors, 'wpTextbox2'); //for edit conflicts
}
}
function getColors (ext) {
if (mw.config.get('wgServer') === 'http://localhost') {
return strongColors;
}
if (ext === '') {
//TODO use window.syntaxHighlighterConfig to configure colors, remove Color there
return defaultWikitextColors;
}
if (ext === 'css') {
return defaultCssColors;
}
if (ext === 'js' || ext === 'lua') { //TODO separate these
return defaultJsColors;
}
}
function getSyntax (ext) {
if (ext === '') {
return wikiSyntax;
}
if (ext === 'css') {
return cssSyntax;
}
if (ext === 'js') {
return jsSyntax;
}
if (ext === 'lua') {
return luaSyntax;
}
}
function killCodeEditor () {
mw.config.set('wgCodeEditorCurrentLanguage', false);
$(function () {
try { //FIXME WTF?
var context = $('#wpTextbox1').data('wikiEditorContext');
$('#wikiEditor-ui-toolbar .group-format [rel="codeEditor"]').remove();
context.fn.disableCodeEditor();
if (mw.user.options.get('usecodeeditor') !== '0') {
context.fn.setCodeEditorPreference(false);
}
/* var $textarea = $('#wpTextbox1');
$textarea.wikiEditor('disableCodeEditor');
$textarea.wikiEditor('removeFromToolbar', {section: 'main', group: 'format', tool: 'codeEditor'}); */
} catch (e) {
}
});
}
function allowTabs () {
$(function () {
var $textarea = $('#wpTextbox1'), scrolltop;
$textarea.keypress(function (e) {
if (e.keyCode === 9 && !(e.ctrlKey || e.altKey)) {
e.preventDefault();
var text = $textarea.textSelection('getSelection'),
sel = $textarea.textSelection('getCaretPosition', {startAndEnd: true}),
lines = text.split('\n'), i, len = 0;
if (text === '') {
if (e.shiftKey) {
text = $textarea.textSelection('getContents');
if (text.charAt(sel[0] - 1) !== '\t') {
return;
}
text = text.substring(0, sel[0] - 1) + text.substring(sel[0]);
scrolltop = $textarea[0].scrollTop;
$textarea.val(text); //$textarea.textSelection('setContents', text);
$textarea.textSelection('setSelection', {start: sel[0] - 1, end: sel[0] - 1});
$textarea[0].scrollTop = scrolltop;
} else {
$textarea.textSelection('encapsulateSelection', {pre: '\t'});
}
return;
}
for (i = 0; i < lines.length; i++) {
if (e.shiftKey) {
if (lines[i].charAt(0) === '\t') {
lines[i] = lines[i].substr(1);
len--;
}
} else {
if (lines[i] !== '') {
lines[i] = '\t' + lines[i];
len++;
}
}
}
if (len !== 0) {
$textarea.textSelection('encapsulateSelection', {peri: lines.join('\n'), replace: true});
$textarea.textSelection('setSelection', {start: sel[0], end: sel[1] + len});
}
}
});
});
}
function init () {
if (!$.client.test(map) && !mw.util.getParamValue('ignoreBlacklist')) {
return;
}
var modelToExt = {
javascript: 'js',
css: 'css',
Scribunto: 'lua'
}, ext, label, deps = [];
if (mw.config.get('wgCanonicalSpecialPageName') !== 'Upload' && mw.user.options.get('usebetatoolbar')) {
deps.push('ext.wikiEditor.toolbar');
mw.util.addCSS('.tool-select .options {z-index: 5;}');
}
switch (mw.config.get('wgUserLanguage')) {
case 'de':
case 'de-at':
case 'de-ch':
case 'de-formal':
label = 'Syntaxhervorhebung aktivieren';
}
ext = modelToExt[mw.config.get('wgPageContentModel')] || '';
if (ext in mw.user.options.get('schnark-syntaxhighlight-exclude', {})) {
return;
}
if (ext) { //FIXME
killCodeEditor();
}
if (ext !== '') {
mw.loader.using('jquery.textSelection', allowTabs);
}
mw.loader.using(deps, function () {
$(function () {
window.setTimeout(function () { //make sure we initialize *after* WikiEditor
initHighlighter(ext, label, mw.user.options.get('schnark-syntaxhighlight-additional', false));
}, 0);
});
});
}
if ($.inArray(mw.config.get('wgAction'), ['edit', 'submit']) !== -1 || mw.config.get('wgCanonicalSpecialPageName') === 'Upload') {
mw.loader.using(['jquery.client', 'mediawiki.util'], init);
}
if (libs.qunit) {
libs.qunit.Parser = Parser;
libs.qunit.wikiSyntax = wikiSyntax;
libs.qunit.jsSyntax = jsSyntax;
libs.qunit.cssSyntax = cssSyntax;
libs.qunit.luaSyntax = luaSyntax;
}
})(jQuery, mediaWiki, mediaWiki.libs);