/samples/scalate-sample-sitegen/src/highlight/highlight.js
JavaScript | 603 lines | 532 code | 54 blank | 17 comment | 145 complexity | e226fbbe9a15eb37f2b794d27eeaf86c MD5 | raw file
- /*
- Syntax highlighting with language autodetection.
- http://softwaremaniacs.org/soft/highlight/
- */
- var hljs = new function() {
- var LANGUAGES = {}
- // selected_languages is used to support legacy mode of selecting languages
- // available for highlighting by passing them as arguments into
- // initHighlighting function. Currently the whole library is expected to
- // contain only those language definitions that are actually get used.
- var selected_languages = {};
- /* Utility functions */
- 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) {
- 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_;
- }
- }
- }
- /* Stream merging */
- 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;
- }
- /* Core highlighting function */
- 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;
- }
- }
- }
- /* Initialization */
- 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;
- }
- /* Public library functions */
- 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);
- })
- }
- // See these 4 lines? This is IE's notion of "block.innerHTML = result". Love this browser :-/
- 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;
- }
- /* Interface definition */
- this.LANGUAGES = LANGUAGES;
- this.initHighlightingOnLoad = initHighlightingOnLoad;
- this.highlightBlock = highlightBlock;
- this.initHighlighting = initHighlighting;
- // Common regexps
- 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 = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~';
- // Common modes
- 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;