|
| 1 | +exports.htmlEscape = function(text) { |
| 2 | + var replacements = {"<": "<", ">": ">", |
| 3 | + "&": "&", "\"": """}; |
| 4 | + return text.replace(/[<>&"]/g, function(character) { |
| 5 | + return replacements[character]; |
| 6 | + }); |
| 7 | +}; |
| 8 | + |
| 9 | +exports.splitLines = function(string){return string.split(/\r?\n/);}; |
| 10 | + |
| 11 | +// Counts the column offset in a string, taking tabs into account. |
| 12 | +// Used mostly to find indentation. |
| 13 | +function countColumn(string, end) { |
| 14 | + if (end == null) { |
| 15 | + end = string.search(/[^\s\u00a0]/); |
| 16 | + if (end == -1) end = string.length; |
| 17 | + } |
| 18 | + for (var i = 0, n = 0; i < end; ++i) { |
| 19 | + if (string.charAt(i) == "\t") n += tabSize - (n % tabSize); |
| 20 | + else ++n; |
| 21 | + } |
| 22 | + return n; |
| 23 | +} |
| 24 | + |
| 25 | +function StringStream(string) { |
| 26 | + this.pos = this.start = 0; |
| 27 | + this.string = string; |
| 28 | +} |
| 29 | +StringStream.prototype = { |
| 30 | + eol: function() {return this.pos >= this.string.length;}, |
| 31 | + sol: function() {return this.pos == 0;}, |
| 32 | + peek: function() {return this.string.charAt(this.pos);}, |
| 33 | + next: function() { |
| 34 | + if (this.pos < this.string.length) |
| 35 | + return this.string.charAt(this.pos++); |
| 36 | + }, |
| 37 | + eat: function(match) { |
| 38 | + var ch = this.string.charAt(this.pos); |
| 39 | + if (typeof match == "string") var ok = ch == match; |
| 40 | + else var ok = ch && (match.test ? match.test(ch) : match(ch)); |
| 41 | + if (ok) {++this.pos; return ch;} |
| 42 | + }, |
| 43 | + eatWhile: function(match) { |
| 44 | + var start = this.pos; |
| 45 | + while (this.eat(match)){} |
| 46 | + return this.pos > start; |
| 47 | + }, |
| 48 | + eatSpace: function() { |
| 49 | + var start = this.pos; |
| 50 | + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; |
| 51 | + return this.pos > start; |
| 52 | + }, |
| 53 | + skipToEnd: function() {this.pos = this.string.length;}, |
| 54 | + skipTo: function(ch) { |
| 55 | + var found = this.string.indexOf(ch, this.pos); |
| 56 | + if (found > -1) {this.pos = found; return true;} |
| 57 | + }, |
| 58 | + backUp: function(n) {this.pos -= n;}, |
| 59 | + column: function() {return countColumn(this.string, this.start);}, |
| 60 | + indentation: function() {return countColumn(this.string);}, |
| 61 | + match: function(pattern, consume, caseInsensitive) { |
| 62 | + if (typeof pattern == "string") { |
| 63 | + function cased(str) {return caseInsensitive ? str.toLowerCase() : str;} |
| 64 | + if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) { |
| 65 | + if (consume !== false) this.pos += pattern.length; |
| 66 | + return true; |
| 67 | + } |
| 68 | + } |
| 69 | + else { |
| 70 | + var match = this.string.slice(this.pos).match(pattern); |
| 71 | + if (match && consume !== false) this.pos += match[0].length; |
| 72 | + return match; |
| 73 | + } |
| 74 | + }, |
| 75 | + current: function(){return this.string.slice(this.start, this.pos);} |
| 76 | +}; |
| 77 | +exports.StringStream = StringStream; |
| 78 | + |
| 79 | +exports.startState = function(mode, a1, a2) { |
| 80 | + return mode.startState ? mode.startState(a1, a2) : true; |
| 81 | +}; |
| 82 | + |
| 83 | +var modes = {}, mimeModes = {}; |
| 84 | +exports.defineMode = function(name, mode) { modes[name] = mode; }; |
| 85 | +exports.defineMIME = function(mime, spec) { mimeModes[mime] = spec; }; |
| 86 | +exports.getMode = function(options, spec) { |
| 87 | + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) |
| 88 | + spec = mimeModes[spec]; |
| 89 | + if (typeof spec == "string") |
| 90 | + var mname = spec, config = {}; |
| 91 | + else if (spec != null) |
| 92 | + var mname = spec.name, config = spec; |
| 93 | + var mfactory = modes[mname]; |
| 94 | + if (!mfactory) throw new Error("Unknown mode: " + spec); |
| 95 | + return mfactory(options, config || {}); |
| 96 | +}; |
| 97 | + |
| 98 | +exports.runMode = function(string, modespec, callback) { |
| 99 | + var mode = exports.getMode({indentUnit: 2}, modespec); |
| 100 | + var isNode = callback.nodeType == 1; |
| 101 | + if (isNode) { |
| 102 | + var node = callback, accum = []; |
| 103 | + callback = function(string, style) { |
| 104 | + if (string == "\n") |
| 105 | + accum.push("<br>"); |
| 106 | + else if (style) |
| 107 | + accum.push("<span class=\"cm-" + exports.htmlEscape(style) + "\">" + exports.htmlEscape(string) + "</span>"); |
| 108 | + else |
| 109 | + accum.push(exports.htmlEscape(string)); |
| 110 | + } |
| 111 | + } |
| 112 | + var lines = exports.splitLines(string), state = exports.startState(mode); |
| 113 | + for (var i = 0, e = lines.length; i < e; ++i) { |
| 114 | + if (i) callback("\n"); |
| 115 | + var stream = new exports.StringStream(lines[i]); |
| 116 | + while (!stream.eol()) { |
| 117 | + var style = mode.token(stream, state); |
| 118 | + callback(stream.current(), style, i, stream.start); |
| 119 | + stream.start = stream.pos; |
| 120 | + } |
| 121 | + } |
| 122 | + if (isNode) |
| 123 | + node.innerHTML = accum.join(""); |
| 124 | +}; |
0 commit comments