You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
578 lines
17 KiB
578 lines
17 KiB
/*
|
|
Syntax highlighting with language autodetection.
|
|
http://softwaremaniacs.org/soft/highlight/
|
|
*/
|
|
|
|
var hljs = new function() {
|
|
var LANGUAGES = {};
|
|
var selected_languages = {};
|
|
function escape(value) {
|
|
return value.replace(/&/gm, '&').replace(/</gm, '<').replace(/>/gm, '>');
|
|
};
|
|
|
|
function contains(array, item) {
|
|
if (!array)
|
|
return false;
|
|
for (var i = 0; i < array.length; i++)
|
|
if (array[i] == item) return true;
|
|
return false;
|
|
};
|
|
|
|
function langRe(language, value, global) {
|
|
var mode = 'm' + (language.case_insensitive ? 'i' : '') + (global ? 'g' : '');
|
|
return new RegExp(value, mode);
|
|
};
|
|
|
|
function findCode(pre) {
|
|
var node;
|
|
for (var i = 0; i < pre.childNodes.length; i++) {
|
|
node = pre.childNodes[i];
|
|
if (node.nodeName == 'CODE')
|
|
return node;
|
|
if (!(node.nodeType == 3 && node.nodeValue.match(/\s+/)))
|
|
return null;
|
|
};
|
|
};
|
|
|
|
function blockText(block) {
|
|
var result = '';
|
|
for (var i = 0; i < block.childNodes.length; i++)
|
|
if (block.childNodes[i].nodeType == 3)
|
|
result += block.childNodes[i].nodeValue;
|
|
else if (block.childNodes[i].nodeName == 'BR')
|
|
result += '\n';
|
|
else
|
|
result += blockText(block.childNodes[i]);
|
|
return result;
|
|
};
|
|
|
|
function blockLanguage(block) {
|
|
var classes = block.className.split(/\s+/);
|
|
classes = classes.concat(block.parentNode.className.split(/\s+/));
|
|
for (var i = 0; i < classes.length; i++) {
|
|
var class_ = classes[i].replace(/^language-/, '');
|
|
if (class_ == 'no-highlight') {
|
|
throw 'No highlight'
|
|
};
|
|
if (LANGUAGES[class_]) {
|
|
return class_;
|
|
};
|
|
};
|
|
};
|
|
function nodeStream(node) {
|
|
var result = [];
|
|
(function (node, offset) {
|
|
for (var i = 0; i < node.childNodes.length; i++) {
|
|
if (node.childNodes[i].nodeType == 3)
|
|
offset += node.childNodes[i].nodeValue.length;
|
|
else if (node.childNodes[i].nodeName == 'BR')
|
|
offset += 1;
|
|
else {
|
|
result.push({
|
|
event: 'start',
|
|
offset: offset,
|
|
node: node.childNodes[i]
|
|
});
|
|
offset = arguments.callee(node.childNodes[i], offset);
|
|
result.push({
|
|
event: 'stop',
|
|
offset: offset,
|
|
node: node.childNodes[i]
|
|
});
|
|
};
|
|
};
|
|
return offset;
|
|
})(node, 0);
|
|
return result;
|
|
};
|
|
|
|
function mergeStreams(stream1, stream2, value) {
|
|
var processed = 0;
|
|
var result = '';
|
|
var nodeStack = [];
|
|
|
|
function selectStream() {
|
|
if (stream1.length && stream2.length) {
|
|
if (stream1[0].offset != stream2[0].offset)
|
|
return (stream1[0].offset < stream2[0].offset) ? stream1 : stream2;
|
|
else
|
|
return (stream1[0].event == 'start' && stream2[0].event == 'stop') ? stream2 : stream1;
|
|
} else {
|
|
return stream1.length ? stream1 : stream2;
|
|
};
|
|
};
|
|
|
|
function open(node) {
|
|
var result = '<' + node.nodeName.toLowerCase();
|
|
for (var i = 0; i < node.attributes.length; i++) {
|
|
var attribute = node.attributes[i];
|
|
result += ' ' + attribute.nodeName.toLowerCase();
|
|
if (attribute.nodeValue != undefined) {
|
|
result += '="' + escape(attribute.nodeValue) + '"';
|
|
};
|
|
};
|
|
return result + '>';
|
|
};
|
|
|
|
function close(node) {
|
|
return '</' + node.nodeName.toLowerCase() + '>';
|
|
};
|
|
|
|
while (stream1.length || stream2.length) {
|
|
var current = selectStream().splice(0, 1)[0];
|
|
result += escape(value.substr(processed, current.offset - processed));
|
|
processed = current.offset;
|
|
if ( current.event == 'start') {
|
|
result += open(current.node);
|
|
nodeStack.push(current.node);
|
|
} else if (current.event == 'stop') {
|
|
var i = nodeStack.length;
|
|
do {
|
|
i--;
|
|
var node = nodeStack[i];
|
|
result += close(node);
|
|
} while (node != current.node);
|
|
nodeStack.splice(i, 1);
|
|
while (i < nodeStack.length) {
|
|
result += open(nodeStack[i]);
|
|
i++;
|
|
};
|
|
};
|
|
};
|
|
result += value.substr(processed);
|
|
return result;
|
|
};
|
|
function highlight(language_name, value) {
|
|
function compileSubModes(mode, language) {
|
|
mode.sub_modes = [];
|
|
for (var i = 0; i < mode.contains.length; i++) {
|
|
for (var j = 0; j < language.modes.length; j++) {
|
|
if (language.modes[j].className == mode.contains[i]) {
|
|
mode.sub_modes[mode.sub_modes.length] = language.modes[j];
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
function subMode(lexem, mode) {
|
|
if (!mode.contains) {
|
|
return null;
|
|
};
|
|
if (!mode.sub_modes) {
|
|
compileSubModes(mode, language);
|
|
};
|
|
for (var i = 0; i < mode.sub_modes.length; i++) {
|
|
if (mode.sub_modes[i].beginRe.test(lexem)) {
|
|
return mode.sub_modes[i];
|
|
};
|
|
};
|
|
return null;
|
|
};
|
|
|
|
function endOfMode(mode_index, lexem) {
|
|
if (modes[mode_index].end && modes[mode_index].endRe.test(lexem))
|
|
return 1;
|
|
if (modes[mode_index].endsWithParent) {
|
|
var level = endOfMode(mode_index - 1, lexem);
|
|
return level ? level + 1 : 0;
|
|
};
|
|
return 0;
|
|
};
|
|
|
|
function isIllegal(lexem, mode) {
|
|
return mode.illegalRe && mode.illegalRe.test(lexem);
|
|
};
|
|
|
|
function compileTerminators(mode, language) {
|
|
var terminators = [];
|
|
|
|
function addTerminator(re) {
|
|
if (!contains(terminators, re)) {
|
|
terminators[terminators.length] = re;
|
|
};
|
|
};
|
|
|
|
if (mode.contains)
|
|
for (var i = 0; i < language.modes.length; i++) {
|
|
if (contains(mode.contains, language.modes[i].className)) {
|
|
addTerminator(language.modes[i].begin);
|
|
};
|
|
};
|
|
|
|
var index = modes.length - 1;
|
|
do {
|
|
if (modes[index].end) {
|
|
addTerminator(modes[index].end);
|
|
};
|
|
index--;
|
|
} while (modes[index + 1].endsWithParent);
|
|
|
|
if (mode.illegal) {
|
|
addTerminator(mode.illegal);
|
|
};
|
|
|
|
var terminator_re = '(' + terminators[0];
|
|
for (var i = 0; i < terminators.length; i++)
|
|
terminator_re += '|' + terminators[i];
|
|
terminator_re += ')';
|
|
return langRe(language, terminator_re);
|
|
};
|
|
|
|
function eatModeChunk(value, index) {
|
|
var mode = modes[modes.length - 1];
|
|
if (!mode.terminators) {
|
|
mode.terminators = compileTerminators(mode, language);
|
|
};
|
|
value = value.substr(index);
|
|
var match = mode.terminators.exec(value);
|
|
if (!match)
|
|
return [value, '', true];
|
|
if (match.index == 0)
|
|
return ['', match[0], false];
|
|
else
|
|
return [value.substr(0, match.index), match[0], false];
|
|
};
|
|
|
|
function keywordMatch(mode, match) {
|
|
var match_str = language.case_insensitive ? match[0].toLowerCase() : match[0];
|
|
for (var className in mode.keywordGroups) {
|
|
if (!mode.keywordGroups.hasOwnProperty(className))
|
|
continue;
|
|
var value = mode.keywordGroups[className].hasOwnProperty(match_str);
|
|
if (value)
|
|
return [className, value];
|
|
};
|
|
return false;
|
|
};
|
|
|
|
function processKeywords(buffer, mode) {
|
|
if (!mode.keywords || !mode.lexems)
|
|
return escape(buffer);
|
|
if (!mode.lexemsRe) {
|
|
var lexems_re = '(' + mode.lexems[0];
|
|
for (var i = 1; i < mode.lexems.length; i++)
|
|
lexems_re += '|' + mode.lexems[i];
|
|
lexems_re += ')';
|
|
mode.lexemsRe = langRe(language, lexems_re, true);
|
|
};
|
|
var result = '';
|
|
var last_index = 0;
|
|
mode.lexemsRe.lastIndex = 0;
|
|
var match = mode.lexemsRe.exec(buffer);
|
|
while (match) {
|
|
result += escape(buffer.substr(last_index, match.index - last_index));
|
|
var keyword_match = keywordMatch(mode, match);
|
|
if (keyword_match) {
|
|
keyword_count += keyword_match[1];
|
|
result += '<span class="'+ keyword_match[0] +'">' + escape(match[0]) + '</span>';
|
|
} else {
|
|
result += escape(match[0]);
|
|
};
|
|
last_index = mode.lexemsRe.lastIndex;
|
|
match = mode.lexemsRe.exec(buffer);
|
|
};
|
|
result += escape(buffer.substr(last_index, buffer.length - last_index));
|
|
return result;
|
|
};
|
|
|
|
function processBuffer(buffer, mode) {
|
|
if (mode.subLanguage && selected_languages[mode.subLanguage]) {
|
|
var result = highlight(mode.subLanguage, buffer);
|
|
keyword_count += result.keyword_count;
|
|
relevance += result.relevance;
|
|
return result.value;
|
|
} else {
|
|
return processKeywords(buffer, mode);
|
|
};
|
|
};
|
|
|
|
function startNewMode(mode, lexem) {
|
|
var markup = mode.noMarkup?'':'<span class="' + mode.displayClassName + '">';
|
|
if (mode.returnBegin) {
|
|
result += markup;
|
|
mode.buffer = '';
|
|
} else if (mode.excludeBegin) {
|
|
result += escape(lexem) + markup;
|
|
mode.buffer = '';
|
|
} else {
|
|
result += markup;
|
|
mode.buffer = lexem;
|
|
};
|
|
modes[modes.length] = mode;
|
|
};
|
|
|
|
function processModeInfo(buffer, lexem, end) {
|
|
var current_mode = modes[modes.length - 1];
|
|
if (end) {
|
|
result += processBuffer(current_mode.buffer + buffer, current_mode);
|
|
return false;
|
|
};
|
|
|
|
var new_mode = subMode(lexem, current_mode);
|
|
if (new_mode) {
|
|
result += processBuffer(current_mode.buffer + buffer, current_mode);
|
|
startNewMode(new_mode, lexem);
|
|
relevance += new_mode.relevance;
|
|
return new_mode.returnBegin;
|
|
};
|
|
|
|
var end_level = endOfMode(modes.length - 1, lexem);
|
|
if (end_level) {
|
|
var markup = current_mode.noMarkup?'':'</span>';
|
|
if (current_mode.returnEnd) {
|
|
result += processBuffer(current_mode.buffer + buffer, current_mode) + markup;
|
|
} else if (current_mode.excludeEnd) {
|
|
result += processBuffer(current_mode.buffer + buffer, current_mode) + markup + escape(lexem);
|
|
} else {
|
|
result += processBuffer(current_mode.buffer + buffer + lexem, current_mode) + markup;
|
|
};
|
|
while (end_level > 1) {
|
|
markup = modes[modes.length - 2].noMarkup?'':'</span>';
|
|
result += markup;
|
|
end_level--;
|
|
modes.length--;
|
|
};
|
|
modes.length--;
|
|
modes[modes.length - 1].buffer = '';
|
|
if (current_mode.starts) {
|
|
for (var i = 0; i < language.modes.length; i++) {
|
|
if (language.modes[i].className == current_mode.starts) {
|
|
startNewMode(language.modes[i], '');
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
return current_mode.returnEnd;
|
|
};
|
|
|
|
if (isIllegal(lexem, current_mode))
|
|
throw 'Illegal';
|
|
};
|
|
|
|
var language = LANGUAGES[language_name];
|
|
var modes = [language.defaultMode];
|
|
var relevance = 0;
|
|
var keyword_count = 0;
|
|
var result = '';
|
|
try {
|
|
var index = 0;
|
|
language.defaultMode.buffer = '';
|
|
do {
|
|
var mode_info = eatModeChunk(value, index);
|
|
var return_lexem = processModeInfo(mode_info[0], mode_info[1], mode_info[2]);
|
|
index += mode_info[0].length;
|
|
if (!return_lexem) {
|
|
index += mode_info[1].length;
|
|
};
|
|
} while (!mode_info[2]);
|
|
if(modes.length > 1)
|
|
throw 'Illegal';
|
|
return {
|
|
relevance: relevance,
|
|
keyword_count: keyword_count,
|
|
value: result
|
|
};
|
|
} catch (e) {
|
|
if (e == 'Illegal') {
|
|
return {
|
|
relevance: 0,
|
|
keyword_count: 0,
|
|
value: escape(value)
|
|
};
|
|
} else {
|
|
throw e;
|
|
};
|
|
};
|
|
};
|
|
function compileModes() {
|
|
for (var i in LANGUAGES) {
|
|
if (!LANGUAGES.hasOwnProperty(i))
|
|
continue;
|
|
var language = LANGUAGES[i];
|
|
for (var j = 0; j < language.modes.length; j++) {
|
|
var mode = language.modes[j];
|
|
if (mode.begin)
|
|
mode.beginRe = langRe(language, '^' + mode.begin);
|
|
if (mode.end)
|
|
mode.endRe = langRe(language, '^' + mode.end);
|
|
if (mode.illegal)
|
|
mode.illegalRe = langRe(language, '^(?:' + mode.illegal + ')');
|
|
language.defaultMode.illegalRe = langRe(language, '^(?:' + language.defaultMode.illegal + ')');
|
|
if (mode.relevance == undefined) {
|
|
mode.relevance = 1;
|
|
};
|
|
if (!mode.displayClassName) {
|
|
mode.displayClassName = mode.className;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
function compileKeywords() {
|
|
|
|
function compileModeKeywords(mode) {
|
|
if (!mode.keywordGroups) {
|
|
for (var key in mode.keywords) {
|
|
if (!mode.keywords.hasOwnProperty(key))
|
|
continue;
|
|
if (mode.keywords[key] instanceof Object)
|
|
mode.keywordGroups = mode.keywords;
|
|
else
|
|
mode.keywordGroups = {'keyword': mode.keywords};
|
|
break;
|
|
};
|
|
};
|
|
};
|
|
|
|
for (var i in LANGUAGES) {
|
|
if (!LANGUAGES.hasOwnProperty(i))
|
|
continue;
|
|
var language = LANGUAGES[i];
|
|
compileModeKeywords(language.defaultMode);
|
|
for (var j = 0; j < language.modes.length; j++) {
|
|
compileModeKeywords(language.modes[j]);
|
|
};
|
|
};
|
|
};
|
|
|
|
function initialize() {
|
|
if (initialize.called)
|
|
return;
|
|
initialize.called = true;
|
|
compileModes();
|
|
compileKeywords();
|
|
selected_languages = LANGUAGES;
|
|
};
|
|
function highlightBlock(block, tabReplace) {
|
|
initialize();
|
|
|
|
try {
|
|
var text = blockText(block);
|
|
var language = blockLanguage(block);
|
|
} catch (e) {
|
|
if (e == 'No highlight')
|
|
return;
|
|
};
|
|
|
|
if (language) {
|
|
var result = highlight(language, text).value;
|
|
} else {
|
|
var max_relevance = 0;
|
|
for (var key in selected_languages) {
|
|
if (!selected_languages.hasOwnProperty(key))
|
|
continue;
|
|
var lang_result = highlight(key, text);
|
|
var relevance = lang_result.keyword_count + lang_result.relevance;
|
|
if (relevance > max_relevance) {
|
|
max_relevance = relevance;
|
|
var result = lang_result.value;
|
|
language = key;
|
|
};
|
|
};
|
|
};
|
|
|
|
if (result) {
|
|
var class_name = block.className;
|
|
if (!class_name.match(language)) {
|
|
class_name += ' ' + language;
|
|
};
|
|
var original = nodeStream(block);
|
|
if (original.length) {
|
|
var pre = document.createElement('pre');
|
|
pre.innerHTML = result;
|
|
result = mergeStreams(original, nodeStream(pre), text);
|
|
};
|
|
if (tabReplace) {
|
|
result = result.replace(/^((<[^>]+>|\t)+)/gm, function(match, p1, offset, s) {
|
|
return p1.replace(/\t/g, tabReplace);
|
|
});
|
|
};
|
|
var container = document.createElement('div');
|
|
container.innerHTML = '<pre><code class="' + class_name + '">' + result + '</code></pre>';
|
|
var environment = block.parentNode.parentNode;
|
|
environment.replaceChild(container.firstChild, block.parentNode);
|
|
};
|
|
};
|
|
|
|
function initHighlighting() {
|
|
if (initHighlighting.called)
|
|
return;
|
|
initHighlighting.called = true;
|
|
initialize();
|
|
if (arguments.length) {
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
if (LANGUAGES[arguments[i]]) {
|
|
selected_languages[arguments[i]] = LANGUAGES[arguments[i]];
|
|
};
|
|
};
|
|
};
|
|
var pres = document.getElementsByTagName('pre');
|
|
for (var i = 0; i < pres.length; i++) {
|
|
var code = findCode(pres[i]);
|
|
if (code)
|
|
highlightBlock(code, hljs.tabReplace);
|
|
};
|
|
};
|
|
|
|
function initHighlightingOnLoad() {
|
|
var original_arguments = arguments;
|
|
var handler = function(){initHighlighting.apply(null, original_arguments)};
|
|
if (window.addEventListener) {
|
|
window.addEventListener('DOMContentLoaded', handler, false);
|
|
window.addEventListener('load', handler, false);
|
|
} else if (window.attachEvent)
|
|
window.attachEvent('onload', handler);
|
|
else
|
|
window.onload = handler;
|
|
};
|
|
this.LANGUAGES = LANGUAGES;
|
|
this.initHighlightingOnLoad = initHighlightingOnLoad;
|
|
this.highlightBlock = highlightBlock;
|
|
this.initHighlighting = initHighlighting;
|
|
|
|
this.IDENT_RE = '[a-zA-Z][a-zA-Z0-9_]*';
|
|
this.UNDERSCORE_IDENT_RE = '[a-zA-Z_][a-zA-Z0-9_]*';
|
|
this.NUMBER_RE = '\\b\\d+(\\.\\d+)?';
|
|
this.C_NUMBER_RE = '\\b(0x[A-Za-z0-9]+|\\d+(\\.\\d+)?)';
|
|
this.RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~';
|
|
|
|
this.APOS_STRING_MODE = {
|
|
className: 'string',
|
|
begin: '\'', end: '\'',
|
|
illegal: '\\n',
|
|
contains: ['escape'],
|
|
relevance: 0
|
|
};
|
|
this.QUOTE_STRING_MODE = {
|
|
className: 'string',
|
|
begin: '"', end: '"',
|
|
illegal: '\\n',
|
|
contains: ['escape'],
|
|
relevance: 0
|
|
};
|
|
this.BACKSLASH_ESCAPE = {
|
|
className: 'escape',
|
|
begin: '\\\\.', end: '^', noMarkup: true,
|
|
relevance: 0
|
|
};
|
|
this.C_LINE_COMMENT_MODE = {
|
|
className: 'comment',
|
|
begin: '//', end: '$',
|
|
relevance: 0
|
|
};
|
|
this.C_BLOCK_COMMENT_MODE = {
|
|
className: 'comment',
|
|
begin: '/\\*', end: '\\*/'
|
|
};
|
|
this.HASH_COMMENT_MODE = {
|
|
className: 'comment',
|
|
begin: '#', end: '$'
|
|
};
|
|
this.C_NUMBER_MODE = {
|
|
className: 'number',
|
|
begin: this.C_NUMBER_RE, end: '^',
|
|
relevance: 0
|
|
};
|
|
}();
|
|
|
|
var initHighlightingOnLoad = hljs.initHighlightingOnLoad;
|
|
|