/**************\ * ____ _____ * DlHighlight -- a JavaScript-based syntax highlighting engine. \ /_ / / * \ / / / * Author: Mihai Bazon, http://mihai.bazon.net/blog \/ /_ / * Copyright: (c) Dynarch.com 2007. All rights reserved. \ / / * http://www.dynarch.com/ / / * \/ * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * \******************************************************************************/ /* * * This thing only cares to colorize a piece of text. It has nothing to do * with the DOM, with reading existing text from the DOM and to insert the * formatted code back. This facility is present in helpers.js. * * Assuming the unformatted code is in the "code" variable, use DlHighlight * this way: * * var hl = new DlHighlight({ lang : "js", * lineNumbers : true }); * * formatted = hl.doItNow(code); * * Now you have in "formatted" the colored version. * * Supported parameters are: * * - "lang" (required) to declare the language * - "lineNumbers" (optional) if you want line numbers * - "showWhitespace" (optional) if you want to display whitespace * in strings as underscores * - "noTrim" (optional) pass *true* if you want not to ignore empty * newlines at the end of the code * */ var DlHighlight = (function(){ var H = function(args) { var self = this; this.args = {}; function D(name, val) { if (name in args) val = args[name]; self.args[name] = val; } D("replaceTabs", null); D("lineNumbers", false); D("noTrim", false); D("showWhitespace", false); var lang = this.lang = H.LANG[args.lang]; this.tokenParsers = lang.tokens.slice(0).reverse(); if (this.args.replaceTabs != null) { var tab = " "; while (--this.args.replaceTabs > 0) tab += " "; this.args.replaceTabs = tab; } }; H.is_ie = /MSIE/.test(navigator.userAgent) && !/Gecko|KHTML|Opera/.test(navigator.userAgent); // definitions useful for most languages out there H.BASE = { COMMENT_CPP : function(txt) { if (txt.charAt(0) == "/" && txt.charAt(1) == "/") { var nl = txt.indexOf("\n"); if (nl == -1) nl = txt.length; var c = this.lang.onComment.call(this, this._he(txt.substring(2, nl))); return { content : { escaped: c }, style : "comment comment-line", type : "comment", index : nl, before : "//" }; } }, COMMENT_C : function(txt) { if (txt.charAt(0) == "/" && txt.charAt(1) == "*") { var nl = txt.indexOf("*/"), c, index = nl; if (nl == -1) nl = index = txt.length; else index += 2; c = this.lang.onComment.call(this, this._he(txt.substring(2, nl))); c = c.replace(/^\s*[*\\|]+/mg, function(s) { return "" + s + ""; }); return { content : { escaped: c }, before : "/*", after : "*/", index : index, style : "comment comment-multiline", type : "comment" }; } }, STRING : { regexp : /^(\x22(\\.|[^\x22\\])*\x22|\x27(\\.|[^\x27\\])*\x27)/g, content : function(m) { m = m[1]; m = m.substr(1, m.length - 2); if (this.args.showWhitespace) m = m.replace(/\x20/g, "_"); return m; }, before : function(m) { return m[1].charAt(0); }, after : function(m) { return m[1].charAt(0); }, type : "string", style : "string" }, PAREN : { regexp : /^[\](){}\[]/g, content : 0, type : "paren", style : "paren" }, OPERATOR : function(txt) { var m = /^[<>!+=%&*\x2f|?:-]+/.exec(txt); if (m && m[0] != "!/") return { content : m[0], index : m.lastIndex, type : "operator", style : "operator" }; } }; H.prototype = { formatToken : function(tok) { var cls = tok.style, html = buffer(); if (cls instanceof Array) cls = cls.join(" "); html(""); if (tok.before) html("", this._he(tok.before), ""); html(this._he(tok.content)); if (tok.after) html("", this._he(tok.after), ""); html(""); return html.get(); }, formatUnknown : function(txt) { return this._he(txt); }, getLastToken : function(pos) { return this.tokens[this.tokens.length - (pos || 0) - 1]; }, lastTokenType : function(re) { var t = this.getLastToken(); if (t) return re.test(t.type); return false; }, parseToken : function(test, code) { var m, tok; if (test.regexp) { test.regexp.lastIndex = 0; m = test.regexp.exec(code); if (m) { tok = { type : test.type, style : test.style, index : test.regexp.lastIndex }; reAdd(this, "before", m, test, tok); reAdd(this, "after", m, test, tok); reAdd(this, "content", m, test, tok); } } else { tok = test.call(this, code); } return tok; }, doItNow : function(code) { this.lang.start.call(this, code); if (!this.args.noTrim) code = code.replace(/\s+$/, ""); var formatted = [], T = this.tokenParsers, m, unknown, tok, i, f = 0, tokens; unknown = ""; tokens = this.tokens = []; while (code.length > 0) { // jumping whitespace one character at a time // might eat a lot of time, let's skip it // quickly m = /^\s+/.exec(code); if (m) { unknown += m[0]; code = code.substr(m[0].length); } for (i = T.length; --i >= 0;) { tok = this.parseToken(T[i], code); if (tok) break; } if (tok) { if (unknown) formatted[f++] = unknown; unknown = ""; if (!(tok instanceof Array)) tok = [ tok ]; var index = 0; tokens.push.apply(tokens, tok); for (var j = 0; j < tok.length; ++j) { var t = tok[j]; formatted[f++] = t; index += getNextIndex(t); } code = code.substr(index); } else { unknown += code.charAt(0); code = code.substr(1); } } if (unknown) formatted[f++] = unknown; for (i = formatted.length; --i >= 0;) { f = formatted[i]; if (typeof f == "string") formatted[i] = this.formatUnknown(f); else formatted[i] = this.formatToken(f); } var html = formatted.join(""); i = this.args.lineNumbers; if (i) { if (typeof i != "number") i = 0; html = html.replace(/^/mg, function() { return "" + (++i) + ""; }); this.args.lineNumbers = i; } // html = html.replace(/\n/g, "
"); this.lang.stop.call(this); return html; }, _he : function(str) { if (str.escaped) return str.escaped; str = str.replace(he_re, function(c) { return he_re_val[c]; }); if (this.args.replaceTabs) str = str.replace(/\t/g, this.args.replaceTabs); return str; } }; var he_re = /[&<>]/g, he_re_val = { "&" : "&", "<" : "<", ">" : ">" }; H.LANG = function(id, tokens) { if (arguments.length > 0) { H.LANG[id] = this; this.tokens = tokens; } }; H.registerLang = function(type, tokens) { F.prototype = new H.LANG; F.prototype.constructor = F; function F() { H.LANG.call(this, type, tokens); }; return new F(); }; var P = H.LANG.prototype; P.start = P.stop = function(){}; P.onComment = function(c) { return makeUrls(c); }; function makeUrls(s) { return s.replace(/\b((https?|ftp):\x2f\x2f[^\s\x22]+)/g, function(url) { return "" + url + ""; }); }; function reAdd(self, c, m, test, tok) { if (test[c] != null) { if (typeof test[c] == "number") { tok[c] = m[test[c]]; } else if (typeof test[c] == "function") { tok[c] = test[c].call(self, m); } else { tok[c] = test[c]; } } } function getNextIndex(tok) { var index = tok.index || 0; if (!index) { // console.log("No index in %s", tok.style); if (tok.before) index += tok.before.length; if (tok.content) index += tok.content.length; if (tok.after) index += tok.after.length; } return index; } var buffer = H.is_ie ? function() { var a = [], idx = 0, f = function() { for (var i = 0; i < arguments.length; ++i) a[idx++] = arguments[i]; }; f.get = function() { return a.join(""); }; return f; } : function() { var str = "", f = function() { str = str.concat.apply(str, arguments); }; f.get = function() { return str; }; return f; }; /**************\ * ____ _____ * DlHighlight -- a JavaScript-based syntax highlighting engine. \ /_ / / * \ / / / * Author: Mihai Bazon, http://mihai.bazon.net/blog \/ /_ / * Copyright: (c) Dynarch.com 2007. All rights reserved. \ / / * http://www.dynarch.com/ / / * \/ * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * \******************************************************************************/ var T = { COMMENT: function(txt) { if (txt.indexOf("", content : txt.substring(4, nl), index : nl + 3, type : "comment", style : "comment" } } }, STRING: function(txt) { if (this.inXmlTag) return this.parseToken(H.BASE.STRING, txt); }, ATTRIBUTE: function(txt) { var r = null; if (this.inXmlTag) { var m = /^([a-z0-9_-]+)(\s*)=/i.exec(txt); if (m) { return [ { content : m[1], style : "builtin xml-attribute" }, { content : m[2] }, // whitespace { content : "=", style : "operator" } ]; } } return r; }, ENTITY: { regexp : /^&(\w+);/g, before : "&", after : ";", content : 1, type : "builtin", style : "builtin xml-entity" }, START_TAG: function(txt) { var m = /^<([a-z0-9_-]+)/i.exec(txt); if (m) { this.inXmlTag = m[1]; return [ { content : "<", style : "paren xml-tagangle" }, { content : m[1], style : "keyword xml-tag xml-tag-open" } ]; } }, END_TAG: function(txt) { var m = /^<\x2f([a-z0-9_-]+)(\s*>)/i.exec(txt); if (m) { return [ { content : "/.exec(txt); if (m) { this.inXmlTag = false; return { content : m[0], style : "paren xml-tagangle" }; } } }; var lang = H.registerLang( "xml", [ T.COMMENT, T.STRING, T.ATTRIBUTE, T.ENTITY, T.START_TAG, T.END_TAG, T.END_ANGLE ]); lang.T = T; lang.start = function() { this.inXmlTag = false; }; /**************\ * ____ _____ * DlHighlight -- a JavaScript-based syntax highlighting engine. \ /_ / / * \ / / / * Author: Mihai Bazon, http://mihai.bazon.net/blog \/ /_ / * Copyright: (c) Dynarch.com 2007. All rights reserved. \ / / * http://www.dynarch.com/ / / * \/ * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * \******************************************************************************/ // Inherits most parsers from XML, but modifies END_ANGLE to highlight SCRIPT tags var re_get_script = /([^\0]*?)<\x2fscript>/gi; var xml = H.LANG.xml; function END_ANGLE(txt) { var m = /^\x2f?>/.exec(txt); if (m) { var tag = this.inXmlTag; this.inXmlTag = false; var tok = [{ content : m[0], style : "paren xml-tagangle" }]; if (/^script$/i.test(tag) && !/><\x2fscript>/i.test(txt)) { re_get_script.lastIndex = 1; var m = re_get_script.exec(txt); if (m && m[1] && m.index == 1) { var code = m[1]; var index = re_get_script.lastIndex - 10; var js = new H({ lang: "js", noTrim: true }).doItNow(code); var jstok = { content : { escaped: js }, style : "xml-inline-script", index : index }; tok.push(jstok); } } return tok; } } H.registerLang("html", [ xml.T.COMMENT, xml.T.STRING, xml.T.ATTRIBUTE, xml.T.ENTITY, xml.T.START_TAG, xml.T.END_TAG, END_ANGLE ]); return H; })(); export default DlHighlight;