123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473 |
- /*
- Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
- Available via Academic Free License >= 2.1 OR the modified BSD license.
- see: http://dojotoolkit.org/license for details
- */
- if(!dojo._hasResource["dojox.highlight._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
- dojo._hasResource["dojox.highlight._base"] = true;
- dojo.provide("dojox.highlight._base");
- /*=====
- dojox.highlight = {
- // summary:
- // Syntax highlighting with language auto-detection package
- //
- // description:
- //
- // Syntax highlighting with language auto-detection package.
- // Released under CLA by the Dojo Toolkit, original BSD release
- // available from: http://softwaremaniacs.org/soft/highlight/
- //
- //
- };
- =====*/
- ;(function(){
- var dh = dojox.highlight,
- C_NUMBER_RE = '\\b(0x[A-Za-z0-9]+|\\d+(\\.\\d+)?)';
-
- // constants
- dh.constants = {
- IDENT_RE: '[a-zA-Z][a-zA-Z0-9_]*',
- UNDERSCORE_IDENT_RE: '[a-zA-Z_][a-zA-Z0-9_]*',
- NUMBER_RE: '\\b\\d+(\\.\\d+)?',
- C_NUMBER_RE: C_NUMBER_RE,
- // Common modes
- APOS_STRING_MODE: {
- className: 'string',
- begin: '\'', end: '\'',
- illegal: '\\n',
- contains: ['escape'],
- relevance: 0
- },
- QUOTE_STRING_MODE: {
- className: 'string',
- begin: '"',
- end: '"',
- illegal: '\\n',
- contains: ['escape'],
- relevance: 0
- },
- BACKSLASH_ESCAPE: {
- className: 'escape',
- begin: '\\\\.', end: '^',
- relevance: 0
- },
- C_LINE_COMMENT_MODE: {
- className: 'comment',
- begin: '//', end: '$',
- relevance: 0
- },
- C_BLOCK_COMMENT_MODE: {
- className: 'comment',
- begin: '/\\*', end: '\\*/'
- },
- HASH_COMMENT_MODE: {
- className: 'comment',
- begin: '#', end: '$'
- },
- C_NUMBER_MODE: {
- className: 'number',
- begin: C_NUMBER_RE, end: '^',
- relevance: 0
- }
- };
- // utilities
-
- function esc(value){
- return value.replace(/&/gm, '&').replace(/</gm, '<').replace(/>/gm, '>');
- }
-
- function verifyText(block){
- return dojo.every(block.childNodes, function(node){
- return node.nodeType == 3 || String(node.nodeName).toLowerCase() == 'br';
- });
- }
- function blockText(block){
- var result = [];
- dojo.forEach(block.childNodes, function(node){
- if(node.nodeType == 3){
- result.push(node.nodeValue);
- }else if(String(node.nodeName).toLowerCase() == 'br'){
- result.push("\n");
- }else{
- throw 'Complex markup';
- }
- });
- return result.join("");
- }
- function buildKeywordGroups(mode){
- if(!mode.keywordGroups){
- for(var key in mode.keywords){
- var kw = mode.keywords[key];
- if(kw instanceof Object){ // dojo.isObject?
- mode.keywordGroups = mode.keywords;
- }else{
- mode.keywordGroups = {keyword: mode.keywords};
- }
- break;
- }
- }
- }
-
- function buildKeywords(lang){
- if(lang.defaultMode && lang.modes){
- buildKeywordGroups(lang.defaultMode);
- dojo.forEach(lang.modes, buildKeywordGroups);
- }
- }
-
- // main object
- var Highlighter = function(langName, textBlock){
- // initialize the state
- this.langName = langName;
- this.lang = dh.languages[langName];
- this.modes = [this.lang.defaultMode];
- this.relevance = 0;
- this.keywordCount = 0;
- this.result = [];
-
- // build resources lazily
- if(!this.lang.defaultMode.illegalRe){
- this.buildRes();
- buildKeywords(this.lang);
- }
-
- // run the algorithm
- try{
- this.highlight(textBlock);
- this.result = this.result.join("");
- }catch(e){
- if(e == 'Illegal'){
- this.relevance = 0;
- this.keywordCount = 0;
- this.partialResult = this.result.join("");
- this.result = esc(textBlock);
- }else{
- throw e;
- }
- }
- };
- dojo.extend(Highlighter, {
- buildRes: function(){
- dojo.forEach(this.lang.modes, function(mode){
- if(mode.begin){
- mode.beginRe = this.langRe('^' + mode.begin);
- }
- if(mode.end){
- mode.endRe = this.langRe('^' + mode.end);
- }
- if(mode.illegal){
- mode.illegalRe = this.langRe('^(?:' + mode.illegal + ')');
- }
- }, this);
- this.lang.defaultMode.illegalRe = this.langRe('^(?:' + this.lang.defaultMode.illegal + ')');
- },
-
- subMode: function(lexeme){
- var classes = this.modes[this.modes.length - 1].contains;
- if(classes){
- var modes = this.lang.modes;
- for(var i = 0; i < classes.length; ++i){
- var className = classes[i];
- for(var j = 0; j < modes.length; ++j){
- var mode = modes[j];
- if(mode.className == className && mode.beginRe.test(lexeme)){ return mode; }
- }
- }
- }
- return null;
- },
- endOfMode: function(lexeme){
- for(var i = this.modes.length - 1; i >= 0; --i){
- var mode = this.modes[i];
- if(mode.end && mode.endRe.test(lexeme)){ return this.modes.length - i; }
- if(!mode.endsWithParent){ break; }
- }
- return 0;
- },
- isIllegal: function(lexeme){
- var illegalRe = this.modes[this.modes.length - 1].illegalRe;
- return illegalRe && illegalRe.test(lexeme);
- },
- langRe: function(value, global){
- var mode = 'm' + (this.lang.case_insensitive ? 'i' : '') + (global ? 'g' : '');
- return new RegExp(value, mode);
- },
-
- buildTerminators: function(){
- var mode = this.modes[this.modes.length - 1],
- terminators = {};
- if(mode.contains){
- dojo.forEach(this.lang.modes, function(lmode){
- if(dojo.indexOf(mode.contains, lmode.className) >= 0){
- terminators[lmode.begin] = 1;
- }
- });
- }
- for(var i = this.modes.length - 1; i >= 0; --i){
- var m = this.modes[i];
- if(m.end){ terminators[m.end] = 1; }
- if(!m.endsWithParent){ break; }
- }
- if(mode.illegal){ terminators[mode.illegal] = 1; }
- var t = [];
- for(i in terminators){ t.push(i); }
- mode.terminatorsRe = this.langRe("(" + t.join("|") + ")");
- },
- eatModeChunk: function(value, index){
- var mode = this.modes[this.modes.length - 1];
-
- // create terminators lazily
- if(!mode.terminatorsRe){
- this.buildTerminators();
- }
-
- value = value.substr(index);
- var match = mode.terminatorsRe.exec(value);
- if(!match){
- return {
- buffer: value,
- lexeme: "",
- end: true
- };
- }
- return {
- buffer: match.index ? value.substr(0, match.index) : "",
- lexeme: match[0],
- end: false
- };
- },
-
- keywordMatch: function(mode, match){
- var matchStr = match[0];
- if(this.lang.case_insensitive){ matchStr = matchStr.toLowerCase(); }
- for(var className in mode.keywordGroups){
- if(matchStr in mode.keywordGroups[className]){ return className; }
- }
- return "";
- },
-
- buildLexemes: function(mode){
- var lexemes = {};
- dojo.forEach(mode.lexems, function(lexeme){
- lexemes[lexeme] = 1;
- });
- var t = [];
- for(var i in lexemes){ t.push(i); }
- mode.lexemsRe = this.langRe("(" + t.join("|") + ")", true);
- },
-
- processKeywords: function(buffer){
- var mode = this.modes[this.modes.length - 1];
- if(!mode.keywords || !mode.lexems){
- return esc(buffer);
- }
-
- // create lexemes lazily
- if(!mode.lexemsRe){
- this.buildLexemes(mode);
- }
-
- mode.lexemsRe.lastIndex = 0;
- var result = [], lastIndex = 0,
- match = mode.lexemsRe.exec(buffer);
- while(match){
- result.push(esc(buffer.substr(lastIndex, match.index - lastIndex)));
- var keywordM = this.keywordMatch(mode, match);
- if(keywordM){
- ++this.keywordCount;
- result.push('<span class="'+ keywordM +'">' + esc(match[0]) + '</span>');
- }else{
- result.push(esc(match[0]));
- }
- lastIndex = mode.lexemsRe.lastIndex;
- match = mode.lexemsRe.exec(buffer);
- }
- result.push(esc(buffer.substr(lastIndex, buffer.length - lastIndex)));
- return result.join("");
- },
-
- processModeInfo: function(buffer, lexeme, end) {
- var mode = this.modes[this.modes.length - 1];
- if(end){
- this.result.push(this.processKeywords(mode.buffer + buffer));
- return;
- }
- if(this.isIllegal(lexeme)){ throw 'Illegal'; }
- var newMode = this.subMode(lexeme);
- if(newMode){
- mode.buffer += buffer;
- this.result.push(this.processKeywords(mode.buffer));
- if(newMode.excludeBegin){
- this.result.push(lexeme + '<span class="' + newMode.className + '">');
- newMode.buffer = '';
- }else{
- this.result.push('<span class="' + newMode.className + '">');
- newMode.buffer = lexeme;
- }
- this.modes.push(newMode);
- this.relevance += typeof newMode.relevance == "number" ? newMode.relevance : 1;
- return;
- }
- var endLevel = this.endOfMode(lexeme);
- if(endLevel){
- mode.buffer += buffer;
- if(mode.excludeEnd){
- this.result.push(this.processKeywords(mode.buffer) + '</span>' + lexeme);
- }else{
- this.result.push(this.processKeywords(mode.buffer + lexeme) + '</span>');
- }
- while(endLevel > 1){
- this.result.push('</span>');
- --endLevel;
- this.modes.pop();
- }
- this.modes.pop();
- this.modes[this.modes.length - 1].buffer = '';
- return;
- }
- },
-
- highlight: function(value){
- var index = 0;
- this.lang.defaultMode.buffer = '';
- do{
- var modeInfo = this.eatModeChunk(value, index);
- this.processModeInfo(modeInfo.buffer, modeInfo.lexeme, modeInfo.end);
- index += modeInfo.buffer.length + modeInfo.lexeme.length;
- }while(!modeInfo.end);
- if(this.modes.length > 1){
- throw 'Illegal';
- }
- }
- });
-
- // more utilities
-
- function replaceText(node, className, text){
- if(String(node.tagName).toLowerCase() == "code" && String(node.parentNode.tagName).toLowerCase() == "pre"){
- // See these 4 lines? This is IE's notion of "node.innerHTML = text". Love this browser :-/
- var container = document.createElement('div'),
- environment = node.parentNode.parentNode;
- container.innerHTML = '<pre><code class="' + className + '">' + text + '</code></pre>';
- environment.replaceChild(container.firstChild, node.parentNode);
- }else{
- node.className = className;
- node.innerHTML = text;
- }
- }
- function highlightStringLanguage(lang, str){
- var highlight = new Highlighter(lang, str);
- return {result:highlight.result, langName:lang, partialResult:highlight.partialResult};
- }
- function highlightLanguage(block, lang){
- var result = highlightStringLanguage(lang, blockText(block));
- replaceText(block, block.className, result.result);
- }
- function highlightStringAuto(str){
- var result = "", langName = "", bestRelevance = 2,
- textBlock = str;
- for(var key in dh.languages){
- if(!dh.languages[key].defaultMode){ continue; } // skip internal members
- var highlight = new Highlighter(key, textBlock),
- relevance = highlight.keywordCount + highlight.relevance, relevanceMax = 0;
- if(!result || relevance > relevanceMax){
- relevanceMax = relevance;
- result = highlight.result;
- langName = highlight.langName;
- }
- }
- return {result:result, langName:langName};
- }
-
- function highlightAuto(block){
- var result = highlightStringAuto(blockText(block));
- if(result.result){
- replaceText(block, result.langName, result.result);
- }
- }
-
- // the public API
- dojox.highlight.processString = function(/* String */ str, /* String? */lang){
- // summary: highlight a string of text
- // returns: Object containing:
- // result - string of html with spans to apply formatting
- // partialResult - if the formating failed: string of html
- // up to the point of the failure, otherwise: undefined
- // langName - the language used to do the formatting
- return lang ? highlightStringLanguage(lang, str) : highlightStringAuto(str);
- };
- dojox.highlight.init = function(/* String|DomNode */ node){
- // summary: Highlight a passed node
- //
- // description:
- //
- // Syntax highlight a passed DomNode or String ID of a DomNode
- //
- //
- // example:
- // | dojox.highlight.init("someId");
- //
- node = dojo.byId(node);
- if(dojo.hasClass(node, "no-highlight")){ return; }
- if(!verifyText(node)){ return; }
-
- var classes = node.className.split(/\s+/),
- flag = dojo.some(classes, function(className){
- if(className.charAt(0) != "_" && dh.languages[className]){
- highlightLanguage(node, className);
- return true; // stop iterations
- }
- return false; // continue iterations
- });
- if(!flag){
- highlightAuto(node);
- }
- };
- /*=====
- dojox.highlight.Code = function(props, node){
- // summary: A Class object to allow for dojoType usage with the highlight engine. This is
- // NOT a Widget in the conventional sense, and does not have any member functions for
- // the instance. This is provided as a convenience. You likely should be calling
- // `dojox.highlight.init` directly.
- //
- // props: Object?
- // Unused. Pass 'null' or {}. Positional usage to allow `dojo.parser` to instantiate
- // this class as other Widgets would be.
- //
- // node: String|DomNode
- // A String ID or DomNode reference to use as the root node of this instance.
- //
- // example:
- // | <pre><code dojoType="dojox.highlight.Code">for(var i in obj){ ... }</code></pre>
- //
- // example:
- // | var inst = new dojox.highlight.Code({}, "someId");
- //
- this.node = dojo.byId(node);
- };
- =====*/
- dh.Code = function(p, n){ dh.init(n); };
- })();
- }
|