1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | if (! this.sh_languages) {
|
8 | this.sh_languages = {};
|
9 | }
|
10 | var sh_requests = {};
|
11 |
|
12 | function sh_isEmailAddress(url) {
|
13 | if (/^mailto:/.test(url)) {
|
14 | return false;
|
15 | }
|
16 | return url.indexOf('@') !== -1;
|
17 | }
|
18 |
|
19 | function sh_setHref(tags, numTags, inputString) {
|
20 | var url = inputString.substring(tags[numTags - 2].pos, tags[numTags - 1].pos);
|
21 | if (url.length >= 2 && url.charAt(0) === '<' && url.charAt(url.length - 1) === '>') {
|
22 | url = url.substr(1, url.length - 2);
|
23 | }
|
24 | if (sh_isEmailAddress(url)) {
|
25 | url = 'mailto:' + url;
|
26 | }
|
27 | tags[numTags - 2].node.href = url;
|
28 | }
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 | function sh_konquerorExec(s) {
|
46 | var result = [''];
|
47 | result.index = s.length;
|
48 | result.input = s;
|
49 | return result;
|
50 | }
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 | function sh_highlightString(inputString, language) {
|
63 | if (/Konqueror/.test(navigator.userAgent)) {
|
64 | if (! language.konquered) {
|
65 | for (var s = 0; s < language.length; s++) {
|
66 | for (var p = 0; p < language[s].length; p++) {
|
67 | var r = language[s][p][0];
|
68 | if (r.source === '$') {
|
69 | r.exec = sh_konquerorExec;
|
70 | }
|
71 | }
|
72 | }
|
73 | language.konquered = true;
|
74 | }
|
75 | }
|
76 |
|
77 | var a = document.createElement('a');
|
78 | var span = document.createElement('span');
|
79 |
|
80 |
|
81 | var tags = [];
|
82 | var numTags = 0;
|
83 |
|
84 |
|
85 | var patternStack = [];
|
86 |
|
87 |
|
88 | var pos = 0;
|
89 |
|
90 |
|
91 | var currentStyle = null;
|
92 |
|
93 | var output = function(s, style) {
|
94 | var length = s.length;
|
95 |
|
96 | if (length === 0) {
|
97 | return;
|
98 | }
|
99 | if (! style) {
|
100 | var stackLength = patternStack.length;
|
101 | if (stackLength !== 0) {
|
102 | var pattern = patternStack[stackLength - 1];
|
103 |
|
104 | if (! pattern[3]) {
|
105 |
|
106 | style = pattern[1];
|
107 | }
|
108 | }
|
109 | }
|
110 | if (currentStyle !== style) {
|
111 | if (currentStyle) {
|
112 | tags[numTags++] = {pos: pos};
|
113 | if (currentStyle === 'sh_url') {
|
114 | sh_setHref(tags, numTags, inputString);
|
115 | }
|
116 | }
|
117 | if (style) {
|
118 | var clone;
|
119 | if (style === 'sh_url') {
|
120 | clone = a.cloneNode(false);
|
121 | }
|
122 | else {
|
123 | clone = span.cloneNode(false);
|
124 | }
|
125 | clone.className = style;
|
126 | tags[numTags++] = {node: clone, pos: pos};
|
127 | }
|
128 | }
|
129 | pos += length;
|
130 | currentStyle = style;
|
131 | };
|
132 |
|
133 | var endOfLinePattern = /\r\n|\r|\n/g;
|
134 | endOfLinePattern.lastIndex = 0;
|
135 | var inputStringLength = inputString.length;
|
136 | while (pos < inputStringLength) {
|
137 | var start = pos;
|
138 | var end;
|
139 | var startOfNextLine;
|
140 | var endOfLineMatch = endOfLinePattern.exec(inputString);
|
141 | if (endOfLineMatch === null) {
|
142 | end = inputStringLength;
|
143 | startOfNextLine = inputStringLength;
|
144 | }
|
145 | else {
|
146 | end = endOfLineMatch.index;
|
147 | startOfNextLine = endOfLinePattern.lastIndex;
|
148 | }
|
149 |
|
150 | var line = inputString.substring(start, end);
|
151 |
|
152 | var matchCache = [];
|
153 | for (;;) {
|
154 | var posWithinLine = pos - start;
|
155 |
|
156 | var stateIndex;
|
157 | var stackLength = patternStack.length;
|
158 | if (stackLength === 0) {
|
159 | stateIndex = 0;
|
160 | }
|
161 | else {
|
162 |
|
163 | stateIndex = patternStack[stackLength - 1][2];
|
164 | }
|
165 |
|
166 | var state = language[stateIndex];
|
167 | var numPatterns = state.length;
|
168 | var mc = matchCache[stateIndex];
|
169 | if (! mc) {
|
170 | mc = matchCache[stateIndex] = [];
|
171 | }
|
172 | var bestMatch = null;
|
173 | var bestPatternIndex = -1;
|
174 | for (var i = 0; i < numPatterns; i++) {
|
175 | var match;
|
176 | if (i < mc.length && (mc[i] === null || posWithinLine <= mc[i].index)) {
|
177 | match = mc[i];
|
178 | }
|
179 | else {
|
180 | var regex = state[i][0];
|
181 | regex.lastIndex = posWithinLine;
|
182 | match = regex.exec(line);
|
183 | mc[i] = match;
|
184 | }
|
185 | if (match !== null && (bestMatch === null || match.index < bestMatch.index)) {
|
186 | bestMatch = match;
|
187 | bestPatternIndex = i;
|
188 | if (match.index === posWithinLine) {
|
189 | break;
|
190 | }
|
191 | }
|
192 | }
|
193 |
|
194 | if (bestMatch === null) {
|
195 | output(line.substring(posWithinLine), null);
|
196 | break;
|
197 | }
|
198 | else {
|
199 |
|
200 | if (bestMatch.index > posWithinLine) {
|
201 | output(line.substring(posWithinLine, bestMatch.index), null);
|
202 | }
|
203 |
|
204 | var pattern = state[bestPatternIndex];
|
205 |
|
206 | var newStyle = pattern[1];
|
207 | var matchedString;
|
208 | if (newStyle instanceof Array) {
|
209 | for (var subexpression = 0; subexpression < newStyle.length; subexpression++) {
|
210 | matchedString = bestMatch[subexpression + 1];
|
211 | output(matchedString, newStyle[subexpression]);
|
212 | }
|
213 | }
|
214 | else {
|
215 | matchedString = bestMatch[0];
|
216 | output(matchedString, newStyle);
|
217 | }
|
218 |
|
219 | switch (pattern[2]) {
|
220 | case -1:
|
221 |
|
222 | break;
|
223 | case -2:
|
224 |
|
225 | patternStack.pop();
|
226 | break;
|
227 | case -3:
|
228 |
|
229 | patternStack.length = 0;
|
230 | break;
|
231 | default:
|
232 |
|
233 | patternStack.push(pattern);
|
234 | break;
|
235 | }
|
236 | }
|
237 | }
|
238 |
|
239 |
|
240 | if (currentStyle) {
|
241 | tags[numTags++] = {pos: pos};
|
242 | if (currentStyle === 'sh_url') {
|
243 | sh_setHref(tags, numTags, inputString);
|
244 | }
|
245 | currentStyle = null;
|
246 | }
|
247 | pos = startOfNextLine;
|
248 | }
|
249 |
|
250 | return tags;
|
251 | }
|
252 |
|
253 |
|
254 |
|
255 |
|
256 | function sh_getClasses(element) {
|
257 | var result = [];
|
258 | var htmlClass = element.className;
|
259 | if (htmlClass && htmlClass.length > 0) {
|
260 | var htmlClasses = htmlClass.split(' ');
|
261 | for (var i = 0; i < htmlClasses.length; i++) {
|
262 | if (htmlClasses[i].length > 0) {
|
263 | result.push(htmlClasses[i]);
|
264 | }
|
265 | }
|
266 | }
|
267 | return result;
|
268 | }
|
269 |
|
270 | function sh_addClass(element, name) {
|
271 | var htmlClasses = sh_getClasses(element);
|
272 | for (var i = 0; i < htmlClasses.length; i++) {
|
273 | if (name.toLowerCase() === htmlClasses[i].toLowerCase()) {
|
274 | return;
|
275 | }
|
276 | }
|
277 | htmlClasses.push(name);
|
278 | element.className = htmlClasses.join(' ');
|
279 | }
|
280 |
|
281 |
|
282 |
|
283 |
|
284 |
|
285 |
|
286 | function sh_extractTagsFromNodeList(nodeList, result) {
|
287 | var length = nodeList.length;
|
288 | for (var i = 0; i < length; i++) {
|
289 | var node = nodeList.item(i);
|
290 | switch (node.nodeType) {
|
291 | case 1:
|
292 | if (node.nodeName.toLowerCase() === 'br') {
|
293 | var terminator;
|
294 | if (/MSIE/.test(navigator.userAgent)) {
|
295 | terminator = '\r';
|
296 | }
|
297 | else {
|
298 | terminator = '\n';
|
299 | }
|
300 | result.text.push(terminator);
|
301 | result.pos++;
|
302 | }
|
303 | else {
|
304 | result.tags.push({node: node.cloneNode(false), pos: result.pos});
|
305 | sh_extractTagsFromNodeList(node.childNodes, result);
|
306 | result.tags.push({pos: result.pos});
|
307 | }
|
308 | break;
|
309 | case 3:
|
310 | case 4:
|
311 | result.text.push(node.data);
|
312 | result.pos += node.length;
|
313 | break;
|
314 | }
|
315 | }
|
316 | }
|
317 |
|
318 |
|
319 |
|
320 |
|
321 |
|
322 |
|
323 |
|
324 |
|
325 |
|
326 |
|
327 | function sh_extractTags(element, tags) {
|
328 | var result = {};
|
329 | result.text = [];
|
330 | result.tags = tags;
|
331 | result.pos = 0;
|
332 | sh_extractTagsFromNodeList(element.childNodes, result);
|
333 | return result.text.join('');
|
334 | }
|
335 |
|
336 |
|
337 |
|
338 |
|
339 |
|
340 |
|
341 |
|
342 | function sh_mergeTags(originalTags, highlightTags) {
|
343 | var numOriginalTags = originalTags.length;
|
344 | if (numOriginalTags === 0) {
|
345 | return highlightTags;
|
346 | }
|
347 |
|
348 | var numHighlightTags = highlightTags.length;
|
349 | if (numHighlightTags === 0) {
|
350 | return originalTags;
|
351 | }
|
352 |
|
353 | var result = [];
|
354 | var originalIndex = 0;
|
355 | var highlightIndex = 0;
|
356 |
|
357 | while (originalIndex < numOriginalTags && highlightIndex < numHighlightTags) {
|
358 | var originalTag = originalTags[originalIndex];
|
359 | var highlightTag = highlightTags[highlightIndex];
|
360 |
|
361 | if (originalTag.pos <= highlightTag.pos) {
|
362 | result.push(originalTag);
|
363 | originalIndex++;
|
364 | }
|
365 | else {
|
366 | result.push(highlightTag);
|
367 | if (highlightTags[highlightIndex + 1].pos <= originalTag.pos) {
|
368 | highlightIndex++;
|
369 | result.push(highlightTags[highlightIndex]);
|
370 | highlightIndex++;
|
371 | }
|
372 | else {
|
373 |
|
374 | result.push({pos: originalTag.pos});
|
375 |
|
376 |
|
377 | highlightTags[highlightIndex] = {node: highlightTag.node.cloneNode(false), pos: originalTag.pos};
|
378 | }
|
379 | }
|
380 | }
|
381 |
|
382 | while (originalIndex < numOriginalTags) {
|
383 | result.push(originalTags[originalIndex]);
|
384 | originalIndex++;
|
385 | }
|
386 |
|
387 | while (highlightIndex < numHighlightTags) {
|
388 | result.push(highlightTags[highlightIndex]);
|
389 | highlightIndex++;
|
390 | }
|
391 |
|
392 | return result;
|
393 | }
|
394 |
|
395 |
|
396 |
|
397 |
|
398 |
|
399 |
|
400 |
|
401 | function sh_insertTags(tags, text) {
|
402 | var doc = document;
|
403 |
|
404 | var result = document.createDocumentFragment();
|
405 | var tagIndex = 0;
|
406 | var numTags = tags.length;
|
407 | var textPos = 0;
|
408 | var textLength = text.length;
|
409 | var currentNode = result;
|
410 |
|
411 |
|
412 | while (textPos < textLength || tagIndex < numTags) {
|
413 | var tag;
|
414 | var tagPos;
|
415 | if (tagIndex < numTags) {
|
416 | tag = tags[tagIndex];
|
417 | tagPos = tag.pos;
|
418 | }
|
419 | else {
|
420 | tagPos = textLength;
|
421 | }
|
422 |
|
423 | if (tagPos <= textPos) {
|
424 |
|
425 | if (tag.node) {
|
426 |
|
427 | var newNode = tag.node;
|
428 | currentNode.appendChild(newNode);
|
429 | currentNode = newNode;
|
430 | }
|
431 | else {
|
432 |
|
433 | currentNode = currentNode.parentNode;
|
434 | }
|
435 | tagIndex++;
|
436 | }
|
437 | else {
|
438 |
|
439 | currentNode.appendChild(doc.createTextNode(text.substring(textPos, tagPos)));
|
440 | textPos = tagPos;
|
441 | }
|
442 | }
|
443 |
|
444 | return result;
|
445 | }
|
446 |
|
447 |
|
448 |
|
449 |
|
450 |
|
451 |
|
452 |
|
453 | function sh_highlightElement(element, language) {
|
454 | sh_addClass(element, 'sh_sourceCode');
|
455 | var originalTags = [];
|
456 | var inputString = sh_extractTags(element, originalTags);
|
457 | var highlightTags = sh_highlightString(inputString, language);
|
458 | var tags = sh_mergeTags(originalTags, highlightTags);
|
459 | var documentFragment = sh_insertTags(tags, inputString);
|
460 | while (element.hasChildNodes()) {
|
461 | element.removeChild(element.firstChild);
|
462 | }
|
463 | element.appendChild(documentFragment);
|
464 | }
|
465 |
|
466 | function sh_getXMLHttpRequest() {
|
467 | if (window.ActiveXObject) {
|
468 | return new ActiveXObject('Msxml2.XMLHTTP');
|
469 | }
|
470 | else if (window.XMLHttpRequest) {
|
471 | return new XMLHttpRequest();
|
472 | }
|
473 | throw 'No XMLHttpRequest implementation available';
|
474 | }
|
475 |
|
476 | function sh_load(language, element, prefix, suffix) {
|
477 | if (language in sh_requests) {
|
478 | sh_requests[language].push(element);
|
479 | return;
|
480 | }
|
481 | sh_requests[language] = [element];
|
482 | var request = sh_getXMLHttpRequest();
|
483 | var url = prefix + 'sh_' + language + suffix;
|
484 | request.open('GET', url, true);
|
485 | request.onreadystatechange = function () {
|
486 | if (request.readyState === 4) {
|
487 | try {
|
488 | if (! request.status || request.status === 200) {
|
489 | eval(request.responseText);
|
490 | var elements = sh_requests[language];
|
491 | for (var i = 0; i < elements.length; i++) {
|
492 | sh_highlightElement(elements[i], sh_languages[language]);
|
493 | }
|
494 | }
|
495 | else {
|
496 | throw 'HTTP error: status ' + request.status;
|
497 | }
|
498 | }
|
499 | finally {
|
500 | request = null;
|
501 | }
|
502 | }
|
503 | };
|
504 | request.send(null);
|
505 | }
|
506 |
|
507 |
|
508 |
|
509 |
|
510 |
|
511 |
|
512 |
|
513 | function highlight(prefix, suffix, tag) {
|
514 | var nodeList = document.getElementsByTagName(tag);
|
515 | for (var i = 0; i < nodeList.length; i++) {
|
516 | var element = nodeList.item(i);
|
517 | var htmlClasses = sh_getClasses(element);
|
518 | var highlighted = false;
|
519 | var donthighlight = false;
|
520 | for (var j = 0; j < htmlClasses.length; j++) {
|
521 | var htmlClass = htmlClasses[j].toLowerCase();
|
522 | if (htmlClass === 'sh_none') {
|
523 | donthighlight = true
|
524 | continue;
|
525 | }
|
526 | if (htmlClass.substr(0, 3) === 'sh_') {
|
527 | var language = htmlClass.substring(3);
|
528 | if (language in sh_languages) {
|
529 | sh_highlightElement(element, sh_languages[language]);
|
530 | highlighted = true;
|
531 | }
|
532 | else if (typeof(prefix) === 'string' && typeof(suffix) === 'string') {
|
533 | sh_load(language, element, prefix, suffix);
|
534 | }
|
535 | else {
|
536 | throw 'Found <' + tag + '> element with class="' + htmlClass + '", but no such language exists';
|
537 | }
|
538 | break;
|
539 | }
|
540 | }
|
541 | if (highlighted === false && donthighlight == false) {
|
542 | sh_highlightElement(element, sh_languages["javascript"]);
|
543 | }
|
544 | }
|
545 | }
|
546 |
|
547 |
|
548 |
|
549 | function sh_highlightDocument(prefix, suffix) {
|
550 | highlight(prefix, suffix, 'tt');
|
551 | highlight(prefix, suffix, 'code');
|
552 | highlight(prefix, suffix, 'pre');
|
553 | }
|