1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | (function(window, document, undefined){
|
11 |
|
12 | var
|
13 |
|
14 |
|
15 | isIe = !+"\v1",
|
16 | EOL = isIe ? "\r" : "\n",
|
17 | EMPTY = function() { return null; },
|
18 | HIGHLIGHTED_NODE_COUNT = 0,
|
19 | DEFAULT_LANGUAGE = "plaintext",
|
20 | DEFAULT_CLASS_PREFIX = "sunlight-",
|
21 |
|
22 |
|
23 | defaultAnalyzer,
|
24 | getComputedStyle,
|
25 | globalOptions = {
|
26 | tabWidth: 4,
|
27 | classPrefix: DEFAULT_CLASS_PREFIX,
|
28 | showWhitespace: false,
|
29 | maxHeight: false
|
30 | },
|
31 | languages = {},
|
32 | languageDefaults = {},
|
33 | events = {
|
34 | beforeHighlightNode: [],
|
35 | beforeHighlight: [],
|
36 | beforeTokenize: [],
|
37 | afterTokenize: [],
|
38 | beforeAnalyze: [],
|
39 | afterAnalyze: [],
|
40 | afterHighlight: [],
|
41 | afterHighlightNode: []
|
42 | };
|
43 |
|
44 | defaultAnalyzer = (function() {
|
45 | function defaultHandleToken(suffix) {
|
46 | return function(context) {
|
47 | var element = document.createElement("span");
|
48 | element.className = context.options.classPrefix + suffix;
|
49 | element.appendChild(context.createTextNode(context.tokens[context.index]));
|
50 | return context.addNode(element) || true;
|
51 | };
|
52 | }
|
53 |
|
54 | return {
|
55 | handleToken: function(context) {
|
56 | return defaultHandleToken(context.tokens[context.index].name)(context);
|
57 | },
|
58 |
|
59 |
|
60 | handle_default: function(context) {
|
61 | return context.addNode(context.createTextNode(context.tokens[context.index]));
|
62 | },
|
63 |
|
64 |
|
65 | handle_ident: function(context) {
|
66 | var evaluate = function(rules, createRule) {
|
67 | var i;
|
68 | rules = rules || [];
|
69 | for (i = 0; i < rules.length; i++) {
|
70 | if (typeof(rules[i]) === "function") {
|
71 | if (rules[i](context)) {
|
72 | return defaultHandleToken("named-ident")(context);
|
73 | }
|
74 | } else if (createRule && createRule(rules[i])(context.tokens)) {
|
75 | return defaultHandleToken("named-ident")(context);
|
76 | }
|
77 | }
|
78 |
|
79 | return false;
|
80 | };
|
81 |
|
82 | return evaluate(context.language.namedIdentRules.custom)
|
83 | || evaluate(context.language.namedIdentRules.follows, function(ruleData) { return createProceduralRule(context.index - 1, -1, ruleData, context.language.caseInsensitive); })
|
84 | || evaluate(context.language.namedIdentRules.precedes, function(ruleData) { return createProceduralRule(context.index + 1, 1, ruleData, context.language.caseInsensitive); })
|
85 | || evaluate(context.language.namedIdentRules.between, function(ruleData) { return createBetweenRule(context.index, ruleData.opener, ruleData.closer, context.language.caseInsensitive); })
|
86 | || defaultHandleToken("ident")(context);
|
87 | }
|
88 | };
|
89 | }());
|
90 |
|
91 | languageDefaults = {
|
92 | analyzer: create(defaultAnalyzer),
|
93 | customTokens: [],
|
94 | namedIdentRules: {},
|
95 | punctuation: /[^\w\s]/,
|
96 | numberParser: defaultNumberParser,
|
97 | caseInsensitive: false,
|
98 | doNotParse: /\s/,
|
99 | contextItems: {},
|
100 | embeddedLanguages: {}
|
101 | };
|
102 |
|
103 |
|
104 | getComputedStyle = (function() {
|
105 | var func = null;
|
106 | if (document.defaultView && document.defaultView.getComputedStyle) {
|
107 | func = document.defaultView.getComputedStyle;
|
108 | } else {
|
109 | func = function(element, anything) {
|
110 | return element["currentStyle"] || {};
|
111 | };
|
112 | }
|
113 |
|
114 | return function(element, style) {
|
115 | return func(element, null)[style];
|
116 | }
|
117 | }());
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 | function createCodeReader(text) {
|
124 | var index = 0,
|
125 | line = 1,
|
126 | column = 1,
|
127 | length,
|
128 | EOF = undefined,
|
129 | currentChar,
|
130 | nextReadBeginsLine;
|
131 |
|
132 | text = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
133 |
|
134 | length = text.length;
|
135 | currentChar = length > 0 ? text.charAt(0) : EOF;
|
136 |
|
137 | function getCharacters(count) {
|
138 | var value;
|
139 | if (count === 0) {
|
140 | return "";
|
141 | }
|
142 |
|
143 | count = count || 1;
|
144 |
|
145 | value = text.substring(index + 1, index + count + 1);
|
146 | return value === "" ? EOF : value;
|
147 | }
|
148 |
|
149 | return {
|
150 | toString: function() {
|
151 | return "length: " + length + ", index: " + index + ", line: " + line + ", column: " + column + ", current: [" + currentChar + "]";
|
152 | },
|
153 |
|
154 | peek: function(count) {
|
155 | return getCharacters(count);
|
156 | },
|
157 |
|
158 | substring: function() {
|
159 | return text.substring(index);
|
160 | },
|
161 |
|
162 | peekSubstring: function() {
|
163 | return text.substring(index + 1);
|
164 | },
|
165 |
|
166 | read: function(count) {
|
167 | var value = getCharacters(count),
|
168 | newlineCount,
|
169 | lastChar;
|
170 |
|
171 | if (value === "") {
|
172 |
|
173 | return value;
|
174 | }
|
175 |
|
176 | if (value !== EOF) {
|
177 |
|
178 | index += value.length;
|
179 | column += value.length;
|
180 |
|
181 |
|
182 | if (nextReadBeginsLine) {
|
183 | line++;
|
184 | column = 1;
|
185 | nextReadBeginsLine = false;
|
186 | }
|
187 |
|
188 | newlineCount = value.substring(0, value.length - 1).replace(/[^\n]/g, "").length;
|
189 | if (newlineCount > 0) {
|
190 | line += newlineCount;
|
191 | column = 1;
|
192 | }
|
193 |
|
194 | lastChar = last(value);
|
195 | if (lastChar === "\n") {
|
196 | nextReadBeginsLine = true;
|
197 | }
|
198 |
|
199 | currentChar = lastChar;
|
200 | } else {
|
201 | index = length;
|
202 | currentChar = EOF;
|
203 | }
|
204 |
|
205 | return value;
|
206 | },
|
207 |
|
208 | text: function() { return text; },
|
209 |
|
210 | getLine: function() { return line; },
|
211 | getColumn: function() { return column; },
|
212 | isEof: function() { return index >= length; },
|
213 | isSol: function() { return column === 1; },
|
214 | isSolWs: function() {
|
215 | var temp = index,
|
216 | c;
|
217 | if (column === 1) {
|
218 | return true;
|
219 | }
|
220 |
|
221 |
|
222 | while ((c = text.charAt(--temp)) !== "") {
|
223 | if (c === "\n") {
|
224 | return true;
|
225 | }
|
226 | if (!/\s/.test(c)) {
|
227 | return false;
|
228 | }
|
229 | }
|
230 |
|
231 | return true;
|
232 | },
|
233 | isEol: function() { return nextReadBeginsLine; },
|
234 | EOF: EOF,
|
235 | current: function() { return currentChar; }
|
236 | };
|
237 | }
|
238 |
|
239 |
|
240 | function create(o) {
|
241 | function F() {}
|
242 | F.prototype = o;
|
243 | return new F();
|
244 | }
|
245 |
|
246 | function appendAll(parent, children) {
|
247 | var i;
|
248 | for (i = 0; i < children.length; i++) {
|
249 | parent.appendChild(children[i]);
|
250 | }
|
251 | }
|
252 |
|
253 |
|
254 | function last(thing) {
|
255 | return thing.charAt ? thing.charAt(thing.length - 1) : thing[thing.length - 1];
|
256 | }
|
257 |
|
258 |
|
259 | function contains(arr, value, caseInsensitive) {
|
260 | var i;
|
261 | if (arr.indexOf && !caseInsensitive) {
|
262 | return arr.indexOf(value) >= 0;
|
263 | }
|
264 |
|
265 | for (i = 0; i < arr.length; i++) {
|
266 | if (arr[i] === value) {
|
267 | return true;
|
268 | }
|
269 |
|
270 | if (caseInsensitive && typeof(arr[i]) === "string" && typeof(value) === "string" && arr[i].toUpperCase() === value.toUpperCase()) {
|
271 | return true;
|
272 | }
|
273 | }
|
274 |
|
275 | return false;
|
276 | }
|
277 |
|
278 |
|
279 | function merge(defaultObject, objectToMerge) {
|
280 | var key;
|
281 | if (!objectToMerge) {
|
282 | return defaultObject;
|
283 | }
|
284 |
|
285 | for (key in objectToMerge) {
|
286 | defaultObject[key] = objectToMerge[key];
|
287 | }
|
288 |
|
289 | return defaultObject;
|
290 | }
|
291 |
|
292 | function clone(object) {
|
293 | return merge({}, object);
|
294 | }
|
295 |
|
296 |
|
297 | function regexEscape(s) {
|
298 | return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
|
299 | }
|
300 |
|
301 | function createProceduralRule(startIndex, direction, tokenRequirements, caseInsensitive) {
|
302 | tokenRequirements = tokenRequirements.slice(0);
|
303 | return function(tokens) {
|
304 | var tokenIndexStart = startIndex,
|
305 | j,
|
306 | expected,
|
307 | actual;
|
308 |
|
309 | if (direction === 1) {
|
310 | tokenRequirements.reverse();
|
311 | }
|
312 |
|
313 | for (j = 0; j < tokenRequirements.length; j++) {
|
314 | actual = tokens[tokenIndexStart + (j * direction)];
|
315 | expected = tokenRequirements[tokenRequirements.length - 1 - j];
|
316 |
|
317 | if (actual === undefined) {
|
318 | if (expected["optional"] !== undefined && expected.optional) {
|
319 | tokenIndexStart -= direction;
|
320 | } else {
|
321 | return false;
|
322 | }
|
323 | } else if (actual.name === expected.token && (expected["values"] === undefined || contains(expected.values, actual.value, caseInsensitive))) {
|
324 |
|
325 | continue;
|
326 | } else if (expected["optional"] !== undefined && expected.optional) {
|
327 | tokenIndexStart -= direction;
|
328 | } else {
|
329 | return false;
|
330 | }
|
331 | }
|
332 |
|
333 | return true;
|
334 | };
|
335 | }
|
336 |
|
337 | function createBetweenRule(startIndex, opener, closer, caseInsensitive) {
|
338 | return function(tokens) {
|
339 | var index = startIndex,
|
340 | token,
|
341 | success = false;
|
342 |
|
343 |
|
344 | while ((token = tokens[--index]) !== undefined) {
|
345 | if (token.name === closer.token && contains(closer.values, token.value)) {
|
346 | if (token.name === opener.token && contains(opener.values, token.value, caseInsensitive)) {
|
347 |
|
348 | success = true;
|
349 | break;
|
350 | }
|
351 |
|
352 | return false;
|
353 | }
|
354 |
|
355 | if (token.name === opener.token && contains(opener.values, token.value, caseInsensitive)) {
|
356 | success = true;
|
357 | break;
|
358 | }
|
359 | }
|
360 |
|
361 | if (!success) {
|
362 | return false;
|
363 | }
|
364 |
|
365 |
|
366 | index = startIndex;
|
367 | while ((token = tokens[++index]) !== undefined) {
|
368 | if (token.name === opener.token && contains(opener.values, token.value, caseInsensitive)) {
|
369 | if (token.name === closer.token && contains(closer.values, token.value, caseInsensitive)) {
|
370 |
|
371 | success = true;
|
372 | break;
|
373 | }
|
374 |
|
375 | return false;
|
376 | }
|
377 |
|
378 | if (token.name === closer.token && contains(closer.values, token.value, caseInsensitive)) {
|
379 | success = true;
|
380 | break;
|
381 | }
|
382 | }
|
383 |
|
384 | return success;
|
385 | };
|
386 | }
|
387 |
|
388 | function matchWord(context, wordMap, tokenName, doNotRead) {
|
389 | var current = context.reader.current(),
|
390 | i,
|
391 | word,
|
392 | peek,
|
393 | line = context.reader.getLine(),
|
394 | column = context.reader.getColumn();
|
395 |
|
396 | wordMap = wordMap || [];
|
397 | if (context.language.caseInsensitive) {
|
398 | current = current.toUpperCase();
|
399 | }
|
400 |
|
401 | if (!wordMap[current]) {
|
402 | return null;
|
403 | }
|
404 |
|
405 | wordMap = wordMap[current];
|
406 | for (i = 0; i < wordMap.length; i++) {
|
407 | word = wordMap[i].value;
|
408 |
|
409 | peek = current + context.reader.peek(word.length);
|
410 | if (word === peek || wordMap[i].regex.test(peek)) {
|
411 | return context.createToken(
|
412 | tokenName,
|
413 | context.reader.current() + context.reader[doNotRead ? "peek" : "read"](word.length - 1),
|
414 | line,
|
415 | column
|
416 | );
|
417 | }
|
418 | }
|
419 |
|
420 | return null;
|
421 | }
|
422 |
|
423 |
|
424 | function getNextWhile(tokens, index, direction, matcher) {
|
425 | var count = 1,
|
426 | token;
|
427 |
|
428 | direction = direction || 1;
|
429 | while (token = tokens[index + (direction * count++)]) {
|
430 | if (!matcher(token)) {
|
431 | return token;
|
432 | }
|
433 | }
|
434 |
|
435 | return undefined;
|
436 | }
|
437 |
|
438 |
|
439 | function createHashMap(wordMap, boundary, caseInsensitive) {
|
440 |
|
441 | var newMap = { },
|
442 | i,
|
443 | word,
|
444 | firstChar;
|
445 |
|
446 | for (i = 0; i < wordMap.length; i++) {
|
447 | word = caseInsensitive ? wordMap[i].toUpperCase() : wordMap[i];
|
448 | firstChar = word.charAt(0);
|
449 | if (!newMap[firstChar]) {
|
450 | newMap[firstChar] = [];
|
451 | }
|
452 |
|
453 | newMap[firstChar].push({ value: word, regex: new RegExp("^" + regexEscape(word) + boundary, caseInsensitive ? "i" : "") });
|
454 | }
|
455 |
|
456 | return newMap;
|
457 | }
|
458 |
|
459 | function defaultNumberParser(context) {
|
460 | var current = context.reader.current(),
|
461 | number,
|
462 | line = context.reader.getLine(),
|
463 | column = context.reader.getColumn(),
|
464 | allowDecimal = true,
|
465 | peek;
|
466 |
|
467 | if (!/\d/.test(current)) {
|
468 |
|
469 | if (current !== "." || !/\d/.test(context.reader.peek())) {
|
470 | return null;
|
471 | }
|
472 |
|
473 |
|
474 | number = current + context.reader.read();
|
475 | allowDecimal = false;
|
476 | } else {
|
477 | number = current;
|
478 | if (current === "0" && context.reader.peek() !== ".") {
|
479 |
|
480 | allowDecimal = false;
|
481 | }
|
482 | }
|
483 |
|
484 |
|
485 |
|
486 |
|
487 |
|
488 | while ((peek = context.reader.peek()) !== context.reader.EOF) {
|
489 | if (!/[A-Za-z0-9]/.test(peek)) {
|
490 | if (peek === "." && allowDecimal && /\d$/.test(context.reader.peek(2))) {
|
491 | number += context.reader.read();
|
492 | allowDecimal = false;
|
493 | continue;
|
494 | }
|
495 |
|
496 | break;
|
497 | }
|
498 |
|
499 | number += context.reader.read();
|
500 | }
|
501 |
|
502 | return context.createToken("number", number, line, column);
|
503 | }
|
504 |
|
505 | function fireEvent(eventName, highlighter, eventContext) {
|
506 | var delegates = events[eventName] || [],
|
507 | i;
|
508 |
|
509 | for (i = 0; i < delegates.length; i++) {
|
510 | delegates[i].call(highlighter, eventContext);
|
511 | }
|
512 | }
|
513 |
|
514 | function Highlighter(options) {
|
515 | this.options = merge(clone(globalOptions), options);
|
516 | }
|
517 |
|
518 | Highlighter.prototype = (function() {
|
519 | var parseNextToken = (function() {
|
520 | function isIdentMatch(context) {
|
521 | return context.language.identFirstLetter && context.language.identFirstLetter.test(context.reader.current());
|
522 | }
|
523 |
|
524 |
|
525 | function parseKeyword(context) {
|
526 | return matchWord(context, context.language.keywords, "keyword");
|
527 | }
|
528 |
|
529 | function parseCustomTokens(context) {
|
530 | var tokenName,
|
531 | token;
|
532 | if (context.language.customTokens === undefined) {
|
533 | return null;
|
534 | }
|
535 |
|
536 | for (tokenName in context.language.customTokens) {
|
537 | token = matchWord(context, context.language.customTokens[tokenName], tokenName);
|
538 | if (token !== null) {
|
539 | return token;
|
540 | }
|
541 | }
|
542 |
|
543 | return null;
|
544 | }
|
545 |
|
546 | function parseOperator(context) {
|
547 | return matchWord(context, context.language.operators, "operator");
|
548 | }
|
549 |
|
550 | function parsePunctuation(context) {
|
551 | var current = context.reader.current();
|
552 | if (context.language.punctuation.test(regexEscape(current))) {
|
553 | return context.createToken("punctuation", current, context.reader.getLine(), context.reader.getColumn());
|
554 | }
|
555 |
|
556 | return null;
|
557 | }
|
558 |
|
559 | function parseIdent(context) {
|
560 | var ident,
|
561 | peek,
|
562 | line = context.reader.getLine(),
|
563 | column = context.reader.getColumn();
|
564 |
|
565 | if (!isIdentMatch(context)) {
|
566 | return null;
|
567 | }
|
568 |
|
569 | ident = context.reader.current();
|
570 | while ((peek = context.reader.peek()) !== context.reader.EOF) {
|
571 | if (!context.language.identAfterFirstLetter.test(peek)) {
|
572 | break;
|
573 | }
|
574 |
|
575 | ident += context.reader.read();
|
576 | }
|
577 |
|
578 | return context.createToken("ident", ident, line, column);
|
579 | }
|
580 |
|
581 | function parseDefault(context) {
|
582 | if (context.defaultData.text === "") {
|
583 |
|
584 | context.defaultData.line = context.reader.getLine();
|
585 | context.defaultData.column = context.reader.getColumn();
|
586 | }
|
587 |
|
588 | context.defaultData.text += context.reader.current();
|
589 | return null;
|
590 | }
|
591 |
|
592 | function parseScopes(context) {
|
593 | var current = context.reader.current(),
|
594 | tokenName,
|
595 | specificScopes,
|
596 | j,
|
597 | opener,
|
598 | line,
|
599 | column,
|
600 | continuation,
|
601 | value;
|
602 |
|
603 | for (tokenName in context.language.scopes) {
|
604 | specificScopes = context.language.scopes[tokenName];
|
605 | for (j = 0; j < specificScopes.length; j++) {
|
606 | opener = specificScopes[j][0];
|
607 |
|
608 | value = current + context.reader.peek(opener.length - 1);
|
609 |
|
610 | if (opener !== value && (!context.language.caseInsensitive || value.toUpperCase() !== opener.toUpperCase())) {
|
611 | continue;
|
612 | }
|
613 |
|
614 | line = context.reader.getLine(), column = context.reader.getColumn();
|
615 | context.reader.read(opener.length - 1);
|
616 | continuation = getScopeReaderFunction(specificScopes[j], tokenName);
|
617 | return continuation(context, continuation, value, line, column);
|
618 | }
|
619 | }
|
620 |
|
621 | return null;
|
622 | }
|
623 |
|
624 | function parseNumber(context) {
|
625 | return context.language.numberParser(context);
|
626 | }
|
627 |
|
628 | function parseCustomRules(context) {
|
629 | var customRules = context.language.customParseRules,
|
630 | i,
|
631 | token;
|
632 |
|
633 | if (customRules === undefined) {
|
634 | return null;
|
635 | }
|
636 |
|
637 | for (i = 0; i < customRules.length; i++) {
|
638 | token = customRules[i](context);
|
639 | if (token) {
|
640 | return token;
|
641 | }
|
642 | }
|
643 |
|
644 | return null;
|
645 | }
|
646 |
|
647 | return function(context) {
|
648 | if (context.language.doNotParse.test(context.reader.current())) {
|
649 | return parseDefault(context);
|
650 | }
|
651 |
|
652 | return parseCustomRules(context)
|
653 | || parseCustomTokens(context)
|
654 | || parseKeyword(context)
|
655 | || parseScopes(context)
|
656 | || parseIdent(context)
|
657 | || parseNumber(context)
|
658 | || parseOperator(context)
|
659 | || parsePunctuation(context)
|
660 | || parseDefault(context);
|
661 | }
|
662 | }());
|
663 |
|
664 | function getScopeReaderFunction(scope, tokenName) {
|
665 | var escapeSequences = scope[2] || [],
|
666 | closerLength = scope[1].length,
|
667 | closer = typeof(scope[1]) === "string" ? new RegExp(regexEscape(scope[1])) : scope[1].regex,
|
668 | zeroWidth = scope[3] || false;
|
669 |
|
670 |
|
671 |
|
672 | return function(context, continuation, buffer, line, column, processCurrent) {
|
673 | var foundCloser = false;
|
674 | buffer = buffer || "";
|
675 |
|
676 | processCurrent = processCurrent ? 1 : 0;
|
677 |
|
678 | function process(processCurrent) {
|
679 |
|
680 | var peekValue,
|
681 | current = context.reader.current(),
|
682 | i;
|
683 |
|
684 | for (i = 0; i < escapeSequences.length; i++) {
|
685 | peekValue = (processCurrent ? current : "") + context.reader.peek(escapeSequences[i].length - processCurrent);
|
686 | if (peekValue === escapeSequences[i]) {
|
687 | buffer += context.reader.read(peekValue.length - processCurrent);
|
688 | return true;
|
689 | }
|
690 | }
|
691 |
|
692 | peekValue = (processCurrent ? current : "") + context.reader.peek(closerLength - processCurrent);
|
693 | if (closer.test(peekValue)) {
|
694 | foundCloser = true;
|
695 | return false;
|
696 | }
|
697 |
|
698 | buffer += processCurrent ? current : context.reader.read();
|
699 | return true;
|
700 | };
|
701 |
|
702 | if (!processCurrent || process(true)) {
|
703 | while (context.reader.peek() !== context.reader.EOF && process(false)) { }
|
704 | }
|
705 |
|
706 | if (processCurrent) {
|
707 | buffer += context.reader.current();
|
708 | context.reader.read();
|
709 | } else {
|
710 | buffer += zeroWidth || context.reader.peek() === context.reader.EOF ? "" : context.reader.read(closerLength);
|
711 | }
|
712 |
|
713 | if (!foundCloser) {
|
714 |
|
715 |
|
716 | context.continuation = continuation;
|
717 | }
|
718 |
|
719 | return context.createToken(tokenName, buffer, line, column);
|
720 | };
|
721 | }
|
722 |
|
723 |
|
724 | function switchToEmbeddedLanguageIfNecessary(context) {
|
725 | var i,
|
726 | embeddedLanguage;
|
727 |
|
728 | for (i = 0; i < context.language.embeddedLanguages.length; i++) {
|
729 | if (!languages[context.language.embeddedLanguages[i].language]) {
|
730 |
|
731 | continue;
|
732 | }
|
733 |
|
734 | embeddedLanguage = clone(context.language.embeddedLanguages[i]);
|
735 |
|
736 | if (embeddedLanguage.switchTo(context)) {
|
737 | embeddedLanguage.oldItems = clone(context.items);
|
738 | context.embeddedLanguageStack.push(embeddedLanguage);
|
739 | context.language = languages[embeddedLanguage.language];
|
740 | context.items = merge(context.items, clone(context.language.contextItems));
|
741 | break;
|
742 | }
|
743 | }
|
744 | }
|
745 |
|
746 |
|
747 | function switchBackFromEmbeddedLanguageIfNecessary(context) {
|
748 | var current = last(context.embeddedLanguageStack),
|
749 | lang;
|
750 |
|
751 | if (current && current.switchBack(context)) {
|
752 | context.language = languages[current.parentLanguage];
|
753 | lang = context.embeddedLanguageStack.pop();
|
754 |
|
755 |
|
756 | context.items = clone(lang.oldItems);
|
757 | lang.oldItems = {};
|
758 | }
|
759 | }
|
760 |
|
761 | function tokenize(unhighlightedCode, language, partialContext, options) {
|
762 | var tokens = [],
|
763 | context,
|
764 | continuation,
|
765 | token;
|
766 |
|
767 | fireEvent("beforeTokenize", this, { code: unhighlightedCode, language: language });
|
768 | context = {
|
769 | reader: createCodeReader(unhighlightedCode),
|
770 | language: language,
|
771 | items: clone(language.contextItems),
|
772 | token: function(index) { return tokens[index]; },
|
773 | getAllTokens: function() { return tokens.slice(0); },
|
774 | count: function() { return tokens.length; },
|
775 | options: options,
|
776 | embeddedLanguageStack: [],
|
777 |
|
778 | defaultData: {
|
779 | text: "",
|
780 | line: 1,
|
781 | column: 1
|
782 | },
|
783 | createToken: function(name, value, line, column) {
|
784 | return {
|
785 | name: name,
|
786 | line: line,
|
787 | value: isIe ? value.replace(/\n/g, "\r") : value,
|
788 | column: column,
|
789 | language: this.language.name
|
790 | };
|
791 | }
|
792 | };
|
793 |
|
794 |
|
795 |
|
796 | if (partialContext.continuation) {
|
797 | continuation = partialContext.continuation;
|
798 | partialContext.continuation = null;
|
799 | tokens.push(continuation(context, continuation, "", context.reader.getLine(), context.reader.getColumn(), true));
|
800 | }
|
801 |
|
802 | while (!context.reader.isEof()) {
|
803 | switchToEmbeddedLanguageIfNecessary(context);
|
804 | token = parseNextToken(context);
|
805 |
|
806 |
|
807 | if (token !== null) {
|
808 | if (context.defaultData.text !== "") {
|
809 | tokens.push(context.createToken("default", context.defaultData.text, context.defaultData.line, context.defaultData.column));
|
810 | context.defaultData.text = "";
|
811 | }
|
812 |
|
813 | if (token[0] !== undefined) {
|
814 |
|
815 | tokens = tokens.concat(token);
|
816 | } else {
|
817 |
|
818 | tokens.push(token);
|
819 | }
|
820 | }
|
821 |
|
822 | switchBackFromEmbeddedLanguageIfNecessary(context);
|
823 | context.reader.read();
|
824 | }
|
825 |
|
826 |
|
827 | if (context.defaultData.text !== "") {
|
828 | tokens.push(context.createToken("default", context.defaultData.text, context.defaultData.line, context.defaultData.column));
|
829 | }
|
830 |
|
831 | fireEvent("afterTokenize", this, { code: unhighlightedCode, parserContext: context });
|
832 | return context;
|
833 | }
|
834 |
|
835 | function createAnalyzerContext(parserContext, partialContext, options) {
|
836 | var nodes = [],
|
837 | prepareText = function() {
|
838 | var nbsp, tab;
|
839 | if (options.showWhitespace) {
|
840 | nbsp = String.fromCharCode(0xB7);
|
841 | tab = new Array(options.tabWidth).join(String.fromCharCode(0x2014)) + String.fromCharCode(0x2192);
|
842 | } else {
|
843 | nbsp = String.fromCharCode(0xA0);
|
844 | tab = new Array(options.tabWidth + 1).join(nbsp);
|
845 | }
|
846 |
|
847 | return function(token) {
|
848 | var value = token.value.split(" ").join(nbsp),
|
849 | tabIndex,
|
850 | lastNewlineColumn,
|
851 | actualColumn,
|
852 | tabLength;
|
853 |
|
854 |
|
855 | while ((tabIndex = value.indexOf("\t")) >= 0) {
|
856 | lastNewlineColumn = value.lastIndexOf(EOL, tabIndex);
|
857 | actualColumn = lastNewlineColumn === -1 ? tabIndex : tabIndex - lastNewlineColumn - 1;
|
858 | tabLength = options.tabWidth - (actualColumn % options.tabWidth);
|
859 |
|
860 | value = value.substring(0, tabIndex) + tab.substring(options.tabWidth - tabLength) + value.substring(tabIndex + 1);
|
861 | }
|
862 |
|
863 | return value;
|
864 | };
|
865 | }();
|
866 |
|
867 | return {
|
868 | tokens: (partialContext.tokens || []).concat(parserContext.getAllTokens()),
|
869 | index: partialContext.index ? partialContext.index + 1 : 0,
|
870 | language: null,
|
871 | getAnalyzer: EMPTY,
|
872 | options: options,
|
873 | continuation: parserContext.continuation,
|
874 | addNode: function(node) { nodes.push(node); },
|
875 | createTextNode: function(token) { return document.createTextNode(prepareText(token)); },
|
876 | getNodes: function() { return nodes; },
|
877 | resetNodes: function() { nodes = []; },
|
878 | items: parserContext.items
|
879 | };
|
880 | }
|
881 |
|
882 |
|
883 |
|
884 | function highlightText(unhighlightedCode, languageId, partialContext) {
|
885 | var language = languages[languageId],
|
886 | analyzerContext;
|
887 |
|
888 | partialContext = partialContext || { };
|
889 | if (language === undefined) {
|
890 |
|
891 | language = languages[DEFAULT_LANGUAGE];
|
892 | }
|
893 |
|
894 | fireEvent("beforeHighlight", this, { code: unhighlightedCode, language: language, previousContext: partialContext });
|
895 |
|
896 | analyzerContext = createAnalyzerContext(
|
897 | tokenize.call(this, unhighlightedCode, language, partialContext, this.options),
|
898 | partialContext,
|
899 | this.options
|
900 | );
|
901 |
|
902 | analyze.call(this, analyzerContext, partialContext.index ? partialContext.index + 1 : 0);
|
903 |
|
904 | fireEvent("afterHighlight", this, { analyzerContext: analyzerContext });
|
905 |
|
906 | return analyzerContext;
|
907 | }
|
908 |
|
909 | function createContainer(ctx) {
|
910 | var container = document.createElement("span");
|
911 | container.className = ctx.options.classPrefix + ctx.language.name;
|
912 | return container;
|
913 | }
|
914 |
|
915 | function analyze(analyzerContext, startIndex) {
|
916 | var nodes,
|
917 | lastIndex,
|
918 | container,
|
919 | i,
|
920 | tokenName,
|
921 | func,
|
922 | language,
|
923 | analyzer;
|
924 |
|
925 | fireEvent("beforeAnalyze", this, { analyzerContext: analyzerContext });
|
926 |
|
927 | if (analyzerContext.tokens.length > 0) {
|
928 | analyzerContext.language = languages[analyzerContext.tokens[0].language] || languages[DEFAULT_LANGUAGE];;
|
929 | nodes = [];
|
930 | lastIndex = 0;
|
931 | container = createContainer(analyzerContext);
|
932 |
|
933 | for (i = startIndex; i < analyzerContext.tokens.length; i++) {
|
934 | language = languages[analyzerContext.tokens[i].language] || languages[DEFAULT_LANGUAGE];
|
935 | if (language.name !== analyzerContext.language.name) {
|
936 | appendAll(container, analyzerContext.getNodes());
|
937 | analyzerContext.resetNodes();
|
938 |
|
939 | nodes.push(container);
|
940 | analyzerContext.language = language;
|
941 | container = createContainer(analyzerContext);
|
942 | }
|
943 |
|
944 | analyzerContext.index = i;
|
945 | tokenName = analyzerContext.tokens[i].name;
|
946 | func = "handle_" + tokenName;
|
947 |
|
948 | analyzer = analyzerContext.getAnalyzer.call(analyzerContext) || analyzerContext.language.analyzer;
|
949 | analyzer[func] ? analyzer[func](analyzerContext) : analyzer.handleToken(analyzerContext);
|
950 | }
|
951 |
|
952 |
|
953 | appendAll(container, analyzerContext.getNodes());
|
954 | nodes.push(container);
|
955 | analyzerContext.resetNodes();
|
956 | for (i = 0; i < nodes.length; i++) {
|
957 | analyzerContext.addNode(nodes[i]);
|
958 | }
|
959 | }
|
960 |
|
961 | fireEvent("afterAnalyze", this, { analyzerContext: analyzerContext });
|
962 | }
|
963 |
|
964 | return {
|
965 |
|
966 | matchSunlightNode: function() {
|
967 | var regex;
|
968 |
|
969 | return function(node) {
|
970 | if (!regex) {
|
971 | regex = new RegExp("(?:\\s|^)" + this.options.classPrefix + "highlight-(\\S+)(?:\\s|$)");
|
972 | }
|
973 |
|
974 | return regex.exec(node.className);
|
975 | };
|
976 | }(),
|
977 |
|
978 |
|
979 | isAlreadyHighlighted: function() {
|
980 | var regex;
|
981 | return function(node) {
|
982 | if (!regex) {
|
983 | regex = new RegExp("(?:\\s|^)" + this.options.classPrefix + "highlighted(?:\\s|$)");
|
984 | }
|
985 |
|
986 | return regex.test(node.className);
|
987 | };
|
988 | }(),
|
989 |
|
990 |
|
991 | highlight: function(code, languageId) { return highlightText.call(this, code, languageId); },
|
992 |
|
993 |
|
994 | highlightNode: function highlightRecursive(node) {
|
995 | var match,
|
996 | languageId,
|
997 | currentNodeCount,
|
998 | j,
|
999 | nodes,
|
1000 | k,
|
1001 | partialContext,
|
1002 | container,
|
1003 | codeContainer;
|
1004 |
|
1005 | if (this.isAlreadyHighlighted(node) || (match = this.matchSunlightNode(node)) === null) {
|
1006 | return;
|
1007 | }
|
1008 |
|
1009 | languageId = match[1];
|
1010 | currentNodeCount = 0;
|
1011 | fireEvent("beforeHighlightNode", this, { node: node });
|
1012 | for (j = 0; j < node.childNodes.length; j++) {
|
1013 | if (node.childNodes[j].nodeType === 3) {
|
1014 |
|
1015 | partialContext = highlightText.call(this, node.childNodes[j].nodeValue, languageId, partialContext);
|
1016 | HIGHLIGHTED_NODE_COUNT++;
|
1017 | currentNodeCount = currentNodeCount || HIGHLIGHTED_NODE_COUNT;
|
1018 | nodes = partialContext.getNodes();
|
1019 |
|
1020 | node.replaceChild(nodes[0], node.childNodes[j]);
|
1021 | for (k = 1; k < nodes.length; k++) {
|
1022 | node.insertBefore(nodes[k], nodes[k - 1].nextSibling);
|
1023 | }
|
1024 | } else if (node.childNodes[j].nodeType === 1) {
|
1025 |
|
1026 | highlightRecursive.call(this, node.childNodes[j]);
|
1027 | }
|
1028 | }
|
1029 |
|
1030 |
|
1031 | node.className += " " + this.options.classPrefix + "highlighted";
|
1032 |
|
1033 |
|
1034 | if (getComputedStyle(node, "display") === "block") {
|
1035 | container = document.createElement("div");
|
1036 | container.className = this.options.classPrefix + "container";
|
1037 |
|
1038 | codeContainer = document.createElement("div");
|
1039 | codeContainer.className = this.options.classPrefix + "code-container";
|
1040 |
|
1041 |
|
1042 | if (this.options.maxHeight !== false) {
|
1043 | codeContainer.style.overflowY = "auto";
|
1044 | codeContainer.style.maxHeight = this.options.maxHeight + (/^\d+$/.test(this.options.maxHeight) ? "px" : "");
|
1045 | }
|
1046 |
|
1047 | container.appendChild(codeContainer);
|
1048 |
|
1049 | node.parentNode.insertBefore(codeContainer, node);
|
1050 | node.parentNode.removeChild(node);
|
1051 | codeContainer.appendChild(node);
|
1052 |
|
1053 | codeContainer.parentNode.insertBefore(container, codeContainer);
|
1054 | codeContainer.parentNode.removeChild(codeContainer);
|
1055 | container.appendChild(codeContainer);
|
1056 | }
|
1057 |
|
1058 | fireEvent("afterHighlightNode", this, {
|
1059 | container: container,
|
1060 | codeContainer: codeContainer,
|
1061 | node: node,
|
1062 | count: currentNodeCount
|
1063 | });
|
1064 | }
|
1065 | };
|
1066 | }());
|
1067 |
|
1068 |
|
1069 | window.Sunlight = {
|
1070 | version: "1.18",
|
1071 | Highlighter: Highlighter,
|
1072 | createAnalyzer: function() { return create(defaultAnalyzer); },
|
1073 | globalOptions: globalOptions,
|
1074 |
|
1075 | highlightAll: function(options) {
|
1076 | var highlighter = new Highlighter(options),
|
1077 | tags = document.getElementsByTagName("*"),
|
1078 | i;
|
1079 |
|
1080 | for (i = 0; i < tags.length; i++) {
|
1081 | highlighter.highlightNode(tags[i]);
|
1082 | }
|
1083 | },
|
1084 |
|
1085 | registerLanguage: function(languageId, languageData) {
|
1086 | var tokenName,
|
1087 | embeddedLanguages,
|
1088 | languageName;
|
1089 |
|
1090 | if (!languageId) {
|
1091 | throw "Languages must be registered with an identifier, e.g. \"php\" for PHP";
|
1092 | }
|
1093 |
|
1094 | languageData = merge(merge({}, languageDefaults), languageData);
|
1095 | languageData.name = languageId;
|
1096 |
|
1097 |
|
1098 | languageData.keywords = createHashMap(languageData.keywords || [], "\\b", languageData.caseInsensitive);
|
1099 | languageData.operators = createHashMap(languageData.operators || [], "", languageData.caseInsensitive);
|
1100 | for (tokenName in languageData.customTokens) {
|
1101 | languageData.customTokens[tokenName] = createHashMap(
|
1102 | languageData.customTokens[tokenName].values,
|
1103 | languageData.customTokens[tokenName].boundary,
|
1104 | languageData.caseInsensitive
|
1105 | );
|
1106 | }
|
1107 |
|
1108 |
|
1109 | embeddedLanguages = [];
|
1110 | for (languageName in languageData.embeddedLanguages) {
|
1111 | embeddedLanguages.push({
|
1112 | parentLanguage: languageData.name,
|
1113 | language: languageName,
|
1114 | switchTo: languageData.embeddedLanguages[languageName].switchTo,
|
1115 | switchBack: languageData.embeddedLanguages[languageName].switchBack
|
1116 | });
|
1117 | }
|
1118 |
|
1119 | languageData.embeddedLanguages = embeddedLanguages;
|
1120 |
|
1121 | languages[languageData.name] = languageData;
|
1122 | },
|
1123 |
|
1124 | isRegistered: function(languageId) { return languages[languageId] !== undefined; },
|
1125 |
|
1126 | bind: function(event, callback) {
|
1127 | if (!events[event]) {
|
1128 | throw "Unknown event \"" + event + "\"";
|
1129 | }
|
1130 |
|
1131 | events[event].push(callback);
|
1132 | },
|
1133 |
|
1134 | util: {
|
1135 | last: last,
|
1136 | regexEscape: regexEscape,
|
1137 | eol: EOL,
|
1138 | clone: clone,
|
1139 | escapeSequences: ["\\n", "\\t", "\\r", "\\\\", "\\v", "\\f"],
|
1140 | contains: contains,
|
1141 | matchWord: matchWord,
|
1142 | createHashMap: createHashMap,
|
1143 | createBetweenRule: createBetweenRule,
|
1144 | createProceduralRule: createProceduralRule,
|
1145 | getNextNonWsToken: function(tokens, index) { return getNextWhile(tokens, index, 1, function(token) { return token.name === "default"; }); },
|
1146 | getPreviousNonWsToken: function(tokens, index) { return getNextWhile(tokens, index, -1, function(token) { return token.name === "default"; }); },
|
1147 | getNextWhile: function(tokens, index, matcher) { return getNextWhile(tokens, index, 1, matcher); },
|
1148 | getPreviousWhile: function(tokens, index, matcher) { return getNextWhile(tokens, index, -1, matcher); },
|
1149 | whitespace: { token: "default", optional: true },
|
1150 | getComputedStyle: getComputedStyle
|
1151 | }
|
1152 | };
|
1153 |
|
1154 |
|
1155 | window.Sunlight.registerLanguage(DEFAULT_LANGUAGE, { punctuation: /(?!x)x/, numberParser: EMPTY });
|
1156 |
|
1157 | }(this, document)); |
\ | No newline at end of file |