1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 2<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 3<head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 5 <title>SyntaxHighlighter Large File Demo</title> 6 <script type="text/javascript" src="../scripts/xregexp.js"></script> <!-- XRegExp is bundled with the final shCore.js during build --> 7 <script type="text/javascript" src="../scripts/shCore.js"></script> 8 <script type="text/javascript" src="../scripts/shBrushJScript.js"></script> 9 <link type="text/css" rel="stylesheet" href="../styles/shCore.css"/> 10 <link type="text/css" rel="Stylesheet" href="../styles/shThemeDefault.css" /> 11 <script type="text/javascript">SyntaxHighlighter.all();</script> 12</head> 13 14<body> 15 16<h1>SyntaxHighlighter Large File Demo</h1> 17 18<p> 19 This demo shows the speed of SyntaxHighlighter and ability to render large files. 20</p> 21 22<script type="text/syntaxhighlighter" class="brush: js;"><![CDATA[ 23 // 24 // Begin anonymous function. This is used to contain local scope variables without polutting global scope. 25 // 26 if (!window.SyntaxHighlighter) var SyntaxHighlighter = function() { 27 28 // Shortcut object which will be assigned to the SyntaxHighlighter variable. 29 // This is a shorthand for local reference in order to avoid long namespace 30 // references to SyntaxHighlighter.whatever... 31 var sh = { 32 defaults : { 33 /** Additional CSS class names to be added to highlighter elements. */ 34 'class-name' : '', 35 36 /** First line number. */ 37 'first-line' : 1, 38 39 /** 40 * Pads line numbers. Possible values are: 41 * 42 * false - don't pad line numbers. 43 * true - automaticaly pad numbers with minimum required number of leading zeroes. 44 * [int] - length up to which pad line numbers. 45 */ 46 'pad-line-numbers' : false, 47 48 /** Lines to highlight. */ 49 'highlight' : null, 50 51 /** Title to be displayed above the code block. */ 52 'title' : null, 53 54 /** Enables or disables smart tabs. */ 55 'smart-tabs' : true, 56 57 /** Gets or sets tab size. */ 58 'tab-size' : 4, 59 60 /** Enables or disables gutter. */ 61 'gutter' : true, 62 63 /** Enables or disables toolbar. */ 64 'toolbar' : true, 65 66 /** Enables quick code copy and paste from double click. */ 67 'quick-code' : true, 68 69 /** Forces code view to be collapsed. */ 70 'collapse' : false, 71 72 /** Enables or disables automatic links. */ 73 'auto-links' : true, 74 75 /** Gets or sets light mode. Equavalent to turning off gutter and toolbar. */ 76 'light' : false, 77 78 'html-script' : false 79 }, 80 81 config : { 82 /** Maximum milliseconds between mouse clicks to consider a double click. */ 83 doubleClickSpeed : 300, 84 85 /** Enables use of <SCRIPT type="syntaxhighlighter" /> tags. */ 86 useScriptTags : true, 87 88 /** Blogger mode flag. */ 89 bloggerMode : false, 90 91 stripBrs : false, 92 93 /** Name of the tag that SyntaxHighlighter will automatically look for. */ 94 tagName : 'pre', 95 96 strings : { 97 expandSource : 'expand', 98 help : '?', 99 alert: 'SyntaxHighlighter\n\n', 100 noBrush : 'Can\'t find brush for: ', 101 brushNotHtmlScript : 'Brush wasn\'t configured for html-script option: ', 102 103 // this is populated by the build script 104 aboutDialog : '@ABOUT@' 105 } 106 }, 107 108 /** Internal 'global' variables. */ 109 vars : { 110 discoveredBrushes : null, 111 highlighters : {} 112 }, 113 114 /** This object is populated by user included external brush files. */ 115 brushes : {}, 116 117 /** Common regular expressions. */ 118 regexLib : { 119 multiLineCComments : /\/\*[\s\S]*?\*\//gm, 120 singleLineCComments : /\/\/.*$/gm, 121 singleLinePerlComments : /#.*$/gm, 122 doubleQuotedString : /"([^\\"\n]|\\.)*"/g, 123 singleQuotedString : /'([^\\'\n]|\\.)*'/g, 124 multiLineDoubleQuotedString : /"([^\\"]|\\.)*"/g, 125 multiLineSingleQuotedString : /'([^\\']|\\.)*'/g, 126 xmlComments : /(<|<)!--[\s\S]*?--(>|>)/gm, 127 url : /\w+:\/\/[\w-.\/?%&=:@;]*/g, 128 129 /** <?= ?> tags. */ 130 phpScriptTags : { left: /(<|<)\?=?/g, right: /\?(>|>)/g }, 131 132 /** <%= %> tags. */ 133 aspScriptTags : { left: /(<|<)%=?/g, right: /%(>|>)/g }, 134 135 /** <script></script> tags. */ 136 scriptScriptTags : { left: /(<|<)\s*script.*?(>|>)/gi, right: /(<|<)\/\s*script\s*(>|>)/gi } 137 }, 138 139 toolbar: { 140 /** 141 * Generates HTML markup for the toolbar. 142 * @param {Highlighter} highlighter Highlighter instance. 143 * @return {String} Returns HTML markup. 144 */ 145 getHtml: function(highlighter) 146 { 147 var html = '<div class="toolbar">', 148 items = sh.toolbar.items, 149 list = items.list 150 ; 151 152 function defaultGetHtml(highlighter, name) 153 { 154 return sh.toolbar.getButtonHtml(highlighter, name, sh.config.strings[name]); 155 }; 156 157 for (var i = 0; i < list.length; i++) 158 html += (items[list[i]].getHtml || defaultGetHtml)(highlighter, list[i]); 159 160 html += '</div>'; 161 162 return html; 163 }, 164 165 /** 166 * Generates HTML markup for a regular button in the toolbar. 167 * @param {Highlighter} highlighter Highlighter instance. 168 * @param {String} commandName Command name that would be executed. 169 * @param {String} label Label text to display. 170 * @return {String} Returns HTML markup. 171 */ 172 getButtonHtml: function(highlighter, commandName, label) 173 { 174 return '<span><a href="#" class="toolbar_item' 175 + ' command_' + commandName 176 + ' ' + commandName 177 + '">' + label + '</a></span>' 178 ; 179 }, 180 181 /** 182 * Event handler for a toolbar anchor. 183 */ 184 handler: function(e) 185 { 186 var target = e.target, 187 className = target.className || '' 188 ; 189 190 function getValue(name) 191 { 192 var r = new RegExp(name + '_(\\w+)'), 193 match = r.exec(className) 194 ; 195 196 return match ? match[1] : null; 197 }; 198 199 var highlighter = getHighlighterById(findParentElement(target, '.syntaxhighlighter').id), 200 commandName = getValue('command') 201 ; 202 203 // execute the toolbar command 204 if (highlighter && commandName) 205 sh.toolbar.items[commandName].execute(highlighter); 206 207 // disable default A click behaviour 208 e.preventDefault(); 209 }, 210 211 /** Collection of toolbar items. */ 212 items : { 213 // Ordered lis of items in the toolbar. Can't expect `for (var n in items)` to be consistent. 214 list: ['expandSource', 'help'], 215 216 expandSource: { 217 getHtml: function(highlighter) 218 { 219 if (highlighter.getParam('collapse') != true) 220 return ''; 221 222 var title = highlighter.getParam('title'); 223 return sh.toolbar.getButtonHtml(highlighter, 'expandSource', title ? title : sh.config.strings.expandSource); 224 }, 225 226 execute: function(highlighter) 227 { 228 var div = getHighlighterDivById(highlighter.id); 229 removeClass(div, 'collapsed'); 230 } 231 }, 232 233 /** Command to display the about dialog window. */ 234 help: { 235 execute: function(highlighter) 236 { 237 var wnd = popup('', '_blank', 500, 250, 'scrollbars=0'), 238 doc = wnd.document 239 ; 240 241 doc.write(sh.config.strings.aboutDialog); 242 doc.close(); 243 wnd.focus(); 244 } 245 } 246 } 247 }, 248 249 /** 250 * Finds all elements on the page which should be processes by SyntaxHighlighter. 251 * 252 * @param {Object} globalParams Optional parameters which override element's 253 * parameters. Only used if element is specified. 254 * 255 * @param {Object} element Optional element to highlight. If none is 256 * provided, all elements in the current document 257 * are returned which qualify. 258 * 259 * @return {Array} Returns list of <code>{ target: DOMElement, params: Object }</code> objects. 260 */ 261 findElements: function(globalParams, element) 262 { 263 var elements = element ? [element] : toArray(document.getElementsByTagName(sh.config.tagName)), 264 conf = sh.config, 265 result = [] 266 ; 267 268 // support for <SCRIPT TYPE="text/syntaxhighlighter" /> feature 269 if (conf.useScriptTags) 270 elements = elements.concat(getSyntaxHighlighterScriptTags()); 271 272 if (elements.length === 0) 273 return result; 274 275 for (var i = 0; i < elements.length; i++) 276 { 277 var item = { 278 target: elements[i], 279 // local params take precedence over globals 280 params: merge(globalParams, parseParams(elements[i].className)) 281 }; 282 283 if (item.params['brush'] == null) 284 continue; 285 286 result.push(item); 287 } 288 289 return result; 290 }, 291 292 /** 293 * Shorthand to highlight all elements on the page that are marked as 294 * SyntaxHighlighter source code. 295 * 296 * @param {Object} globalParams Optional parameters which override element's 297 * parameters. Only used if element is specified. 298 * 299 * @param {Object} element Optional element to highlight. If none is 300 * provided, all elements in the current document 301 * are highlighted. 302 */ 303 highlight: function(globalParams, element) 304 { 305 var elements = this.findElements(globalParams, element), 306 propertyName = 'innerHTML', 307 highlighter = null, 308 conf = sh.config 309 ; 310 311 if (elements.length === 0) 312 return; 313 314 for (var i = 0; i < elements.length; i++) 315 { 316 var element = elements[i], 317 target = element.target, 318 params = element.params, 319 brushName = params.brush, 320 code 321 ; 322 323 if (brushName == null) 324 continue; 325 326 // Instantiate a brush 327 if (params['html-script'] == 'true' || sh.defaults['html-script'] == true) 328 { 329 highlighter = new sh.HtmlScript(brushName); 330 brushName = 'htmlscript'; 331 } 332 else 333 { 334 var brush = findBrush(brushName); 335 336 if (brush) 337 highlighter = new brush(); 338 else 339 continue; 340 } 341 342 code = target[propertyName]; 343 344 // remove CDATA from <SCRIPT/> tags if it's present 345 if (conf.useScriptTags) 346 code = stripCData(code); 347 348 // Inject title if the attribute is present 349 if ((target.title || '') != '') 350 params.title = target.title; 351 352 params['brush'] = brushName; 353 highlighter.init(params); 354 element = highlighter.getDiv(code); 355 target.parentNode.replaceChild(element, target); 356 } 357 }, 358 359 /** 360 * Main entry point for the SyntaxHighlighter. 361 * @param {Object} params Optional params to apply to all highlighted elements. 362 */ 363 all: function(params) 364 { 365 attachEvent( 366 window, 367 'load', 368 function() { sh.highlight(params); } 369 ); 370 } 371 }; // end of sh 372 373 /** 374 * Checks if target DOM elements has specified CSS class. 375 * @param {DOMElement} target Target DOM element to check. 376 * @param {String} className Name of the CSS class to check for. 377 * @return {Boolean} Returns true if class name is present, false otherwise. 378 */ 379 function hasClass(target, className) 380 { 381 return target.className.indexOf(className) != -1; 382 }; 383 384 /** 385 * Adds CSS class name to the target DOM element. 386 * @param {DOMElement} target Target DOM element. 387 * @param {String} className New CSS class to add. 388 */ 389 function addClass(target, className) 390 { 391 if (!hasClass(target, className)) 392 target.className += ' ' + className; 393 }; 394 395 /** 396 * Removes CSS class name from the target DOM element. 397 * @param {DOMElement} target Target DOM element. 398 * @param {String} className CSS class to remove. 399 */ 400 function removeClass(target, className) 401 { 402 target.className = target.className.replace(className, ''); 403 }; 404 405 /** 406 * Converts the source to array object. Mostly used for function arguments and 407 * lists returned by getElementsByTagName() which aren't Array objects. 408 * @param {List} source Source list. 409 * @return {Array} Returns array. 410 */ 411 function toArray(source) 412 { 413 var result = []; 414 415 for (var i = 0; i < source.length; i++) 416 result.push(source[i]); 417 418 return result; 419 }; 420 421 /** 422 * Splits block of text into lines. 423 * @param {String} block Block of text. 424 * @return {Array} Returns array of lines. 425 */ 426 function splitLines(block) 427 { 428 return block.split('\n'); 429 } 430 431 /** 432 * Generates HTML ID for the highlighter. 433 * @param {String} highlighterId Highlighter ID. 434 * @return {String} Returns HTML ID. 435 */ 436 function getHighlighterId(id) 437 { 438 var prefix = 'highlighter_'; 439 return id.indexOf(prefix) == 0 ? id : prefix + id; 440 }; 441 442 /** 443 * Finds Highlighter instance by ID. 444 * @param {String} highlighterId Highlighter ID. 445 * @return {Highlighter} Returns instance of the highlighter. 446 */ 447 function getHighlighterById(id) 448 { 449 return sh.vars.highlighters[getHighlighterId(id)]; 450 }; 451 452 /** 453 * Finds highlighter's DIV container. 454 * @param {String} highlighterId Highlighter ID. 455 * @return {Element} Returns highlighter's DIV element. 456 */ 457 function getHighlighterDivById(id) 458 { 459 return document.getElementById(getHighlighterId(id)); 460 }; 461 462 /** 463 * Stores highlighter so that getHighlighterById() can do its thing. Each 464 * highlighter must call this method to preserve itself. 465 * @param {Highilghter} highlighter Highlighter instance. 466 */ 467 function storeHighlighter(highlighter) 468 { 469 sh.vars.highlighters[getHighlighterId(highlighter.id)] = highlighter; 470 }; 471 472 /** 473 * Looks for a child or parent node which has specified classname. 474 * Equivalent to jQuery's $(container).find(".className") 475 * @param {Element} target Target element. 476 * @param {String} search Class name or node name to look for. 477 * @param {Boolean} reverse If set to true, will go up the node tree instead of down. 478 * @return {Element} Returns found child or parent element on null. 479 */ 480 function findElement(target, search, reverse /* optional */) 481 { 482 if (target == null) 483 return null; 484 485 var nodes = reverse != true ? target.childNodes : [ target.parentNode ], 486 propertyToFind = { '#' : 'id', '.' : 'className' }[search.substr(0, 1)] || 'nodeName', 487 expectedValue, 488 found 489 ; 490 491 expectedValue = propertyToFind != 'nodeName' 492 ? search.substr(1) 493 : search.toUpperCase() 494 ; 495 496 // main return of the found node 497 if ((target[propertyToFind] || '').indexOf(expectedValue) != -1) 498 return target; 499 500 for (var i = 0; nodes && i < nodes.length && found == null; i++) 501 found = findElement(nodes[i], search, reverse); 502 503 return found; 504 }; 505 506 /** 507 * Looks for a parent node which has specified classname. 508 * This is an alias to <code>findElement(container, className, true)</code>. 509 * @param {Element} target Target element. 510 * @param {String} className Class name to look for. 511 * @return {Element} Returns found parent element on null. 512 */ 513 function findParentElement(target, className) 514 { 515 return findElement(target, className, true); 516 }; 517 518 /** 519 * Finds an index of element in the array. 520 * @ignore 521 * @param {Object} searchElement 522 * @param {Number} fromIndex 523 * @return {Number} Returns index of element if found; -1 otherwise. 524 */ 525 function indexOf(array, searchElement, fromIndex) 526 { 527 fromIndex = Math.max(fromIndex || 0, 0); 528 529 for (var i = fromIndex; i < array.length; i++) 530 if(array[i] == searchElement) 531 return i; 532 533 return -1; 534 }; 535 536 /** 537 * Generates a unique element ID. 538 */ 539 function guid(prefix) 540 { 541 return (prefix || '') + Math.round(Math.random() * 1000000).toString(); 542 }; 543 544 /** 545 * Merges two objects. Values from obj2 override values in obj1. 546 * Function is NOT recursive and works only for one dimensional objects. 547 * @param {Object} obj1 First object. 548 * @param {Object} obj2 Second object. 549 * @return {Object} Returns combination of both objects. 550 */ 551 function merge(obj1, obj2) 552 { 553 var result = {}, name; 554 555 for (name in obj1) 556 result[name] = obj1[name]; 557 558 for (name in obj2) 559 result[name] = obj2[name]; 560 561 return result; 562 }; 563 564 /** 565 * Attempts to convert string to boolean. 566 * @param {String} value Input string. 567 * @return {Boolean} Returns true if input was "true", false if input was "false" and value otherwise. 568 */ 569 function toBoolean(value) 570 { 571 var result = { "true" : true, "false" : false }[value]; 572 return result == null ? value : result; 573 }; 574 575 /** 576 * Opens up a centered popup window. 577 * @param {String} url URL to open in the window. 578 * @param {String} name Popup name. 579 * @param {int} width Popup width. 580 * @param {int} height Popup height. 581 * @param {String} options window.open() options. 582 * @return {Window} Returns window instance. 583 */ 584 function popup(url, name, width, height, options) 585 { 586 var x = (screen.width - width) / 2, 587 y = (screen.height - height) / 2 588 ; 589 590 options += ', left=' + x + 591 ', top=' + y + 592 ', width=' + width + 593 ', height=' + height 594 ; 595 options = options.replace(/^,/, ''); 596 597 var win = window.open(url, name, options); 598 win.focus(); 599 return win; 600 }; 601 602 /** 603 * Adds event handler to the target object. 604 * @param {Object} obj Target object. 605 * @param {String} type Name of the event. 606 * @param {Function} func Handling function. 607 */ 608 function attachEvent(obj, type, func, scope) 609 { 610 function handler(e) 611 { 612 e = e || window.event; 613 614 if (!e.target) 615 { 616 e.target = e.srcElement; 617 e.preventDefault = function() 618 { 619 this.returnValue = false; 620 }; 621 } 622 623 func.call(scope || window, e); 624 }; 625 626 if (obj.attachEvent) 627 { 628 obj.attachEvent('on' + type, handler); 629 } 630 else 631 { 632 obj.addEventListener(type, handler, false); 633 } 634 }; 635 636 /** 637 * Displays an alert. 638 * @param {String} str String to display. 639 */ 640 function alert(str) 641 { 642 window.alert(sh.config.strings.alert + str); 643 }; 644 645 /** 646 * Finds a brush by its alias. 647 * 648 * @param {String} alias Brush alias. 649 * @param {Boolean} showAlert Suppresses the alert if false. 650 * @return {Brush} Returns bursh constructor if found, null otherwise. 651 */ 652 function findBrush(alias, showAlert) 653 { 654 var brushes = sh.vars.discoveredBrushes, 655 result = null 656 ; 657 658 if (brushes == null) 659 { 660 brushes = {}; 661 662 // Find all brushes 663 for (var brush in sh.brushes) 664 { 665 var aliases = sh.brushes[brush].aliases; 666 667 if (aliases == null) 668 continue; 669 670 // keep the brush name 671 sh.brushes[brush].name = brush.toLowerCase(); 672 673 for (var i = 0; i < aliases.length; i++) 674 brushes[aliases[i]] = brush; 675 } 676 677 sh.vars.discoveredBrushes = brushes; 678 } 679 680 result = sh.brushes[brushes[alias]]; 681 682 if (result == null && showAlert != false) 683 alert(sh.config.strings.noBrush + alias); 684 685 return result; 686 }; 687 688 /** 689 * Executes a callback on each line and replaces each line with result from the callback. 690 * @param {Object} str Input string. 691 * @param {Object} callback Callback function taking one string argument and returning a string. 692 */ 693 function eachLine(str, callback) 694 { 695 var lines = splitLines(str); 696 697 for (var i = 0; i < lines.length; i++) 698 lines[i] = callback(lines[i], i); 699 700 return lines.join('\n'); 701 }; 702 703 /** 704 * This is a special trim which only removes first and last empty lines 705 * and doesn't affect valid leading space on the first line. 706 * 707 * @param {String} str Input string 708 * @return {String} Returns string without empty first and last lines. 709 */ 710 function trimFirstAndLastLines(str) 711 { 712 return str.replace(/^[ ]*[\n]+|[\n]*[ ]*$/g, ''); 713 }; 714 715 /** 716 * Parses key/value pairs into hash object. 717 * 718 * Understands the following formats: 719 * - name: word; 720 * - name: [word, word]; 721 * - name: "string"; 722 * - name: 'string'; 723 * 724 * For example: 725 * name1: value; name2: [value, value]; name3: 'value' 726 * 727 * @param {String} str Input string. 728 * @return {Object} Returns deserialized object. 729 */ 730 function parseParams(str) 731 { 732 var match, 733 result = {}, 734 arrayRegex = XRegExp("^\\[(?<values>(.*?))\\]$"), 735 regex = XRegExp( 736 "(?<name>[\\w-]+)" + 737 "\\s*:\\s*" + 738 "(?<value>" + 739 "[\\w%#-]+|" + // word 740 "\\[.*?\\]|" + // [] array 741 '".*?"|' + // "" string 742 "'.*?'" + // '' string 743 ")\\s*;?", 744 "g" 745 ) 746 ; 747 748 while ((match = XRegExp.exec(str, regex)) != null) 749 { 750 var value = match.value 751 .replace(/^['"]|['"]$/g, '') // strip quotes from end of strings 752 ; 753 754 // try to parse array value 755 if (value != null && arrayRegex.test(value)) 756 { 757 var m = XRegExp.exec(value, arrayRegex); 758 value = m.values.length > 0 ? m.values.split(/\s*,\s*/) : []; 759 } 760 761 result[match.name] = value; 762 } 763 764 return result; 765 }; 766 767 /** 768 * Wraps each line of the string into <code/> tag with given style applied to it. 769 * 770 * @param {String} str Input string. 771 * @param {String} css Style name to apply to the string. 772 * @return {String} Returns input string with each line surrounded by <span/> tag. 773 */ 774 function wrapLinesWithCode(str, css) 775 { 776 if (str == null || str.length == 0 || str == '\n') 777 return str; 778 779 str = str.replace(/</g, '<'); 780 781 // Replace two or more sequential spaces with leaving last space untouched. 782 str = str.replace(/ {2,}/g, function(m) 783 { 784 var spaces = ''; 785 786 for (var i = 0; i < m.length - 1; i++) 787 spaces += '\u00A0'; 788 789 return spaces + ' '; 790 }); 791 792 // Split each line and apply <span class="...">...</span> to them so that 793 // leading spaces aren't included. 794 if (css != null) 795 str = eachLine(str, function(line) 796 { 797 if (line.length == 0) 798 return ''; 799 800 var spaces = ''; 801 802 line = line.replace(/^( | )+/, function(s) 803 { 804 spaces = s; 805 return ''; 806 }); 807 808 if (line.length == 0) 809 return spaces; 810 811 return spaces + '<code class="' + css + '">' + line + '</code>'; 812 }); 813 814 return str; 815 }; 816 817 /** 818 * Pads number with zeros until it's length is the same as given length. 819 * 820 * @param {Number} number Number to pad. 821 * @param {Number} length Max string length with. 822 * @return {String} Returns a string padded with proper amount of '0'. 823 */ 824 function padNumber(number, length) 825 { 826 var result = number.toString(); 827 828 while (result.length < length) 829 result = '0' + result; 830 831 return result; 832 }; 833 834 /** 835 * Replaces tabs with spaces. 836 * 837 * @param {String} code Source code. 838 * @param {Number} tabSize Size of the tab. 839 * @return {String} Returns code with all tabs replaces by spaces. 840 */ 841 function processTabs(code, tabSize) 842 { 843 var tab = ''; 844 845 for (var i = 0; i < tabSize; i++) 846 tab += ' '; 847 848 return code.replace(/\t/g, tab); 849 }; 850 851 /** 852 * Replaces tabs with smart spaces. 853 * 854 * @param {String} code Code to fix the tabs in. 855 * @param {Number} tabSize Number of spaces in a column. 856 * @return {String} Returns code with all tabs replaces with roper amount of spaces. 857 */ 858 function processSmartTabs(code, tabSize) 859 { 860 var lines = splitLines(code), 861 tab = '\t', 862 spaces = '' 863 ; 864 865 // Create a string with 1000 spaces to copy spaces from... 866 // It's assumed that there would be no indentation longer than that. 867 for (var i = 0; i < 50; i++) 868 spaces += ' '; // 20 spaces * 50 869 870 // This function inserts specified amount of spaces in the string 871 // where a tab is while removing that given tab. 872 function insertSpaces(line, pos, count) 873 { 874 return line.substr(0, pos) 875 + spaces.substr(0, count) 876 + line.substr(pos + 1, line.length) // pos + 1 will get rid of the tab 877 ; 878 }; 879 880 // Go through all the lines and do the 'smart tabs' magic. 881 code = eachLine(code, function(line) 882 { 883 if (line.indexOf(tab) == -1) 884 return line; 885 886 var pos = 0; 887 888 while ((pos = line.indexOf(tab)) != -1) 889 { 890 // This is pretty much all there is to the 'smart tabs' logic. 891 // Based on the position within the line and size of a tab, 892 // calculate the amount of spaces we need to insert. 893 var spaces = tabSize - pos % tabSize; 894 line = insertSpaces(line, pos, spaces); 895 } 896 897 return line; 898 }); 899 900 return code; 901 }; 902 903 /** 904 * Performs various string fixes based on configuration. 905 */ 906 function fixInputString(str) 907 { 908 var br = /<br\s*\/?>|<br\s*\/?>/gi; 909 910 if (sh.config.bloggerMode == true) 911 str = str.replace(br, '\n'); 912 913 if (sh.config.stripBrs == true) 914 str = str.replace(br, ''); 915 916 return str; 917 }; 918 919 /** 920 * Removes all white space at the begining and end of a string. 921 * 922 * @param {String} str String to trim. 923 * @return {String} Returns string without leading and following white space characters. 924 */ 925 function trim(str) 926 { 927 return str.replace(/^\s+|\s+$/g, ''); 928 }; 929 930 /** 931 * Unindents a block of text by the lowest common indent amount. 932 * @param {String} str Text to unindent. 933 * @return {String} Returns unindented text block. 934 */ 935 function unindent(str) 936 { 937 var lines = splitLines(fixInputString(str)), 938 indents = new Array(), 939 regex = /^\s*/, 940 min = 1000 941 ; 942 943 // go through every line and check for common number of indents 944 for (var i = 0; i < lines.length && min > 0; i++) 945 { 946 var line = lines[i]; 947 948 if (trim(line).length == 0) 949 continue; 950 951 var matches = regex.exec(line); 952 953 // In the event that just one line doesn't have leading white space 954 // we can't unindent anything, so bail completely. 955 if (matches == null) 956 return str; 957 958 min = Math.min(matches[0].length, min); 959 } 960 961 // trim minimum common number of white space from the begining of every line 962 if (min > 0) 963 for (var i = 0; i < lines.length; i++) 964 lines[i] = lines[i].substr(min); 965 966 return lines.join('\n'); 967 }; 968 969 /** 970 * Callback method for Array.sort() which sorts matches by 971 * index position and then by length. 972 * 973 * @param {Match} m1 Left object. 974 * @param {Match} m2 Right object. 975 * @return {Number} Returns -1, 0 or -1 as a comparison result. 976 */ 977 function matchesSortCallback(m1, m2) 978 { 979 // sort matches by index first 980 if(m1.index < m2.index) 981 return -1; 982 else if(m1.index > m2.index) 983 return 1; 984 else 985 { 986 // if index is the same, sort by length 987 if(m1.length < m2.length) 988 return -1; 989 else if(m1.length > m2.length) 990 return 1; 991 } 992 993 return 0; 994 }; 995 996 /** 997 * Executes given regular expression on provided code and returns all 998 * matches that are found. 999 * 1000 * @param {String} code Code to execute regular expression on. 1001 * @param {Object} regex Regular expression item info from <code>regexList</code> collection. 1002 * @return {Array} Returns a list of Match objects. 1003 */ 1004 function getMatches(code, regexInfo) 1005 { 1006 function defaultAdd(match, regexInfo) 1007 { 1008 return match[0]; 1009 }; 1010 1011 var index = 0, 1012 match = null, 1013 matches = [], 1014 func = regexInfo.func ? regexInfo.func : defaultAdd 1015 ; 1016 1017 while((match = XRegExp.exec(code, regexInfo.regex)) != null) 1018 { 1019 var resultMatch = func(match, regexInfo); 1020 1021 if (typeof(resultMatch) == 'string') 1022 resultMatch = [new sh.Match(resultMatch, match.index, regexInfo.css)]; 1023 1024 matches = matches.concat(resultMatch); 1025 } 1026 1027 return matches; 1028 }; 1029 1030 /** 1031 * Turns all URLs in the code into <a/> tags. 1032 * @param {String} code Input code. 1033 * @return {String} Returns code with </a> tags. 1034 */ 1035 function processUrls(code) 1036 { 1037 var gt = /(.*)((>|<).*)/; 1038 1039 return code.replace(sh.regexLib.url, function(m) 1040 { 1041 var suffix = '', 1042 match = null 1043 ; 1044 1045 // We include < and > in the URL for the common cases like <http://google.com> 1046 // The problem is that they get transformed into <http://google.com> 1047 // Where as > easily looks like part of the URL string. 1048 1049 if (match = gt.exec(m)) 1050 { 1051 m = match[1]; 1052 suffix = match[2]; 1053 } 1054 1055 return '<a href="' + m + '">' + m + '</a>' + suffix; 1056 }); 1057 }; 1058 1059 /** 1060 * Finds all <SCRIPT TYPE="text/syntaxhighlighter" /> elementss. 1061 * Finds both "text/syntaxhighlighter" and "syntaxhighlighter" 1062 * ...in order to make W3C validator happy with subtype and backwardscompatible without subtype 1063 * @return {Array} Returns array of all found SyntaxHighlighter tags. 1064 */ 1065 function getSyntaxHighlighterScriptTags() 1066 { 1067 var tags = document.getElementsByTagName('script'), 1068 result = [] 1069 ; 1070 1071 for (var i = 0; i < tags.length; i++) 1072 if (tags[i].type == 'text/syntaxhighlighter' || tags[i].type == 'syntaxhighlighter') 1073 result.push(tags[i]); 1074 1075 return result; 1076 }; 1077 1078 /** 1079 * Strips <![CDATA[]]> from <SCRIPT /> content because it should be used 1080 * there in most cases for XHTML compliance. 1081 * @param {String} original Input code. 1082 * @return {String} Returns code without leading <![CDATA[]]> tags. 1083 */ 1084 function stripCData(original) 1085 { 1086 var left = '<![CDATA[', 1087 right = ']]>', 1088 // for some reason IE inserts some leading blanks here 1089 copy = trim(original), 1090 changed = false, 1091 leftLength = left.length, 1092 rightLength = right.length 1093 ; 1094 1095 if (copy.indexOf(left) == 0) 1096 { 1097 copy = copy.substring(leftLength); 1098 changed = true; 1099 } 1100 1101 var copyLength = copy.length; 1102 1103 if (copy.indexOf(right) == copyLength - rightLength) 1104 { 1105 copy = copy.substring(0, copyLength - rightLength); 1106 changed = true; 1107 } 1108 1109 return changed ? copy : original; 1110 }; 1111 1112 /** 1113 * Match object. 1114 */ 1115 sh.Match = function(value, index, css) 1116 { 1117 this.value = value; 1118 this.index = index; 1119 this.length = value.length; 1120 this.css = css; 1121 this.brushName = null; 1122 }; 1123 1124 sh.Match.prototype.toString = function() 1125 { 1126 return this.value; 1127 }; 1128 1129 /** 1130 * Simulates HTML code with a scripting language embedded. 1131 * 1132 * @param {String} scriptBrushName Brush name of the scripting language. 1133 */ 1134 sh.HtmlScript = function(scriptBrushName) 1135 { 1136 var brushClass = findBrush(scriptBrushName), 1137 scriptBrush, 1138 xmlBrush = new sh.brushes.Xml(), 1139 bracketsRegex = null, 1140 ref = this, 1141 methodsToExpose = 'getDiv getHtml init'.split(' ') 1142 ; 1143 1144 if (brushClass == null) 1145 return; 1146 1147 scriptBrush = new brushClass(); 1148 1149 for(var i = 0; i < methodsToExpose.length; i++) 1150 // make a closure so we don't lose the name after i changes 1151 (function() { 1152 var name = methodsToExpose[i]; 1153 1154 ref[name] = function() 1155 { 1156 return xmlBrush[name].apply(xmlBrush, arguments); 1157 }; 1158 })(); 1159 1160 if (scriptBrush.htmlScript == null) 1161 { 1162 alert(sh.config.strings.brushNotHtmlScript + scriptBrushName); 1163 return; 1164 } 1165 1166 xmlBrush.regexList.push( 1167 { regex: scriptBrush.htmlScript.code, func: process } 1168 ); 1169 1170 function offsetMatches(matches, offset) 1171 { 1172 for (var j = 0; j < matches.length; j++) 1173 matches[j].index += offset; 1174 } 1175 1176 function process(match, info) 1177 { 1178 var code = match.code, 1179 matches = [], 1180 regexList = scriptBrush.regexList, 1181 offset = match.index + match.left.length, 1182 htmlScript = scriptBrush.htmlScript, 1183 result 1184 ; 1185 1186 // add all matches from the code 1187 for (var i = 0; i < regexList.length; i++) 1188 { 1189 result = getMatches(code, regexList[i]); 1190 offsetMatches(result, offset); 1191 matches = matches.concat(result); 1192 } 1193 1194 // add left script bracket 1195 if (htmlScript.left != null && match.left != null) 1196 { 1197 result = getMatches(match.left, htmlScript.left); 1198 offsetMatches(result, match.index); 1199 matches = matches.concat(result); 1200 } 1201 1202 // add right script bracket 1203 if (htmlScript.right != null && match.right != null) 1204 { 1205 result = getMatches(match.right, htmlScript.right); 1206 offsetMatches(result, match.index + match[0].lastIndexOf(match.right)); 1207 matches = matches.concat(result); 1208 } 1209 1210 for (var j = 0; j < matches.length; j++) 1211 matches[j].brushName = brushClass.name; 1212 1213 return matches; 1214 } 1215 }; 1216 1217 /** 1218 * Main Highlither class. 1219 * @constructor 1220 */ 1221 sh.Highlighter = function() 1222 { 1223 // not putting any code in here because of the prototype inheritance 1224 }; 1225 1226 sh.Highlighter.prototype = { 1227 /** 1228 * Quick code mouse double click handler. 1229 */ 1230 quickCodeHandler: function(e) 1231 { 1232 var target = e.target, 1233 highlighterDiv = findParentElement(target, '.syntaxhighlighter'), 1234 container = findParentElement(target, '.container'), 1235 textarea = document.createElement('textarea'), 1236 highlighter 1237 ; 1238 1239 if (!container || !highlighterDiv || findElement(container, 'textarea')) 1240 return; 1241 1242 highlighter = getHighlighterById(highlighterDiv.id); 1243 1244 // add source class name 1245 addClass(highlighterDiv, 'source'); 1246 1247 var tmp = document.createElement('div'), 1248 code = unindent(trimFirstAndLastLines(highlighter.code)) 1249 ; 1250 1251 // using a temp div we "htmldecode()" our code, ie we are grabbing text value 1252 code = code.replace(/</g, '<'); // insure that all angle brackets are escaped 1253 tmp.innerHTML = code; 1254 code = tmp.childNodes[0].nodeValue; 1255 delete tmp; 1256 1257 // inject <textarea/> tag 1258 textarea.value = code; 1259 container.appendChild(textarea); 1260 1261 // preselect all text 1262 textarea.focus(); 1263 textarea.select(); 1264 1265 // set up handler for lost focus 1266 attachEvent(textarea, 'blur', function(e) 1267 { 1268 textarea.parentNode.removeChild(textarea); 1269 removeClass(highlighterDiv, 'source'); 1270 }); 1271 }, 1272 1273 /** 1274 * Returns value of the parameter passed to the highlighter. 1275 * @param {String} name Name of the parameter. 1276 * @param {Object} defaultValue Default value. 1277 * @return {Object} Returns found value or default value otherwise. 1278 */ 1279 getParam: function(name, defaultValue) 1280 { 1281 var result = this.params[name]; 1282 return toBoolean(result == null ? defaultValue : result); 1283 }, 1284 1285 /** 1286 * Shortcut to document.createElement(). 1287 * @param {String} name Name of the element to create (DIV, A, etc). 1288 * @return {HTMLElement} Returns new HTML element. 1289 */ 1290 create: function(name) 1291 { 1292 return document.createElement(name); 1293 }, 1294 1295 /** 1296 * Applies all regular expression to the code and stores all found 1297 * matches in the `this.matches` array. 1298 * @param {Array} regexList List of regular expressions. 1299 * @param {String} code Source code. 1300 * @return {Array} Returns list of matches. 1301 */ 1302 findMatches: function(regexList, code) 1303 { 1304 var result = []; 1305 1306 if (regexList != null) 1307 for (var i = 0; i < regexList.length; i++) 1308 // BUG: length returns len+1 for array if methods added to prototype chain (oising@gmail.com) 1309 if (typeof (regexList[i]) == "object") 1310 result = result.concat(getMatches(code, regexList[i])); 1311 1312 // sort and remove nested the matches 1313 return this.removeNestedMatches(result.sort(matchesSortCallback)); 1314 }, 1315 1316 /** 1317 * Checks to see if any of the matches are inside of other matches. 1318 * This process would get rid of highligted strings inside comments, 1319 * keywords inside strings and so on. 1320 */ 1321 removeNestedMatches: function(matches) 1322 { 1323 // Optimized by Jose Prado (http://joseprado.com) 1324 for (var i = 0; i < matches.length; i++) 1325 { 1326 if (matches[i] === null) 1327 continue; 1328 1329 var itemI = matches[i], 1330 itemIEndPos = itemI.index + itemI.length 1331 ; 1332 1333 for (var j = i + 1; j < matches.length && matches[i] !== null; j++) 1334 { 1335 var itemJ = matches[j]; 1336 1337 if (itemJ === null) 1338 continue; 1339 else if (itemJ.index > itemIEndPos) 1340 break; 1341 else if (itemJ.index == itemI.index && itemJ.length > itemI.length) 1342 matches[i] = null; 1343 else if (itemJ.index >= itemI.index && itemJ.index < itemIEndPos) 1344 matches[j] = null; 1345 } 1346 } 1347 1348 return matches; 1349 }, 1350 1351 /** 1352 * Creates an array containing integer line numbers starting from the 'first-line' param. 1353 * @return {Array} Returns array of integers. 1354 */ 1355 figureOutLineNumbers: function(code) 1356 { 1357 var lines = [], 1358 firstLine = parseInt(this.getParam('first-line')) 1359 ; 1360 1361 eachLine(code, function(line, index) 1362 { 1363 lines.push(index + firstLine); 1364 }); 1365 1366 return lines; 1367 }, 1368 1369 /** 1370 * Determines if specified line number is in the highlighted list. 1371 */ 1372 isLineHighlighted: function(lineNumber) 1373 { 1374 var list = this.getParam('highlight', []); 1375 1376 if (typeof(list) != 'object' && list.push == null) 1377 list = [ list ]; 1378 1379 return indexOf(list, lineNumber.toString()) != -1; 1380 }, 1381 1382 /** 1383 * Generates HTML markup for a single line of code while determining alternating line style. 1384 * @param {Integer} lineNumber Line number. 1385 * @param {String} code Line HTML markup. 1386 * @return {String} Returns HTML markup. 1387 */ 1388 getLineHtml: function(lineIndex, lineNumber, code) 1389 { 1390 var classes = [ 1391 'line', 1392 'number' + lineNumber, 1393 'index' + lineIndex, 1394 'alt' + (lineNumber % 2 == 0 ? 1 : 2).toString() 1395 ]; 1396 1397 if (this.isLineHighlighted(lineNumber)) 1398 classes.push('highlighted'); 1399 1400 if (lineNumber == 0) 1401 classes.push('break'); 1402 1403 return '<div class="' + classes.join(' ') + '">' + code + '</div>'; 1404 }, 1405 1406 /** 1407 * Generates HTML markup for line number column. 1408 * @param {String} code Complete code HTML markup. 1409 * @param {Array} lineNumbers Calculated line numbers. 1410 * @return {String} Returns HTML markup. 1411 */ 1412 getLineNumbersHtml: function(code, lineNumbers) 1413 { 1414 var html = '', 1415 count = splitLines(code).length, 1416 firstLine = parseInt(this.getParam('first-line')), 1417 pad = this.getParam('pad-line-numbers') 1418 ; 1419 1420 if (pad == true) 1421 pad = (firstLine + count - 1).toString().length; 1422 else if (isNaN(pad) == true) 1423 pad = 0; 1424 1425 for (var i = 0; i < count; i++) 1426 { 1427 var lineNumber = lineNumbers ? lineNumbers[i] : firstLine + i, 1428 code = lineNumber == 0 ? '\u00A0' : padNumber(lineNumber, pad) 1429 ; 1430 1431 html += this.getLineHtml(i, lineNumber, code); 1432 } 1433 1434 return html; 1435 }, 1436 1437 /** 1438 * Splits block of text into individual DIV lines. 1439 * @param {String} code Code to highlight. 1440 * @param {Array} lineNumbers Calculated line numbers. 1441 * @return {String} Returns highlighted code in HTML form. 1442 */ 1443 getCodeLinesHtml: function(html, lineNumbers) 1444 { 1445 html = trim(html); 1446 1447 var lines = splitLines(html), 1448 padLength = this.getParam('pad-line-numbers'), 1449 firstLine = parseInt(this.getParam('first-line')), 1450 html = '', 1451 brushName = this.getParam('brush') 1452 ; 1453 1454 for (var i = 0; i < lines.length; i++) 1455 { 1456 var line = lines[i], 1457 indent = /^( |\s)+/.exec(line), 1458 spaces = null, 1459 lineNumber = lineNumbers ? lineNumbers[i] : firstLine + i; 1460 ; 1461 1462 if (indent != null) 1463 { 1464 spaces = indent[0].toString(); 1465 line = line.substr(spaces.length); 1466 spaces = spaces.replace(' ', '\u00A0'); 1467 } 1468 1469 line = trim(line); 1470 1471 if (line.length == 0) 1472 line = '\u00A0'; 1473 1474 html += this.getLineHtml( 1475 i, 1476 lineNumber, 1477 (spaces != null ? '<code class="' + brushName + ' spaces">' + spaces + '</code>' : '') + line 1478 ); 1479 } 1480 1481 return html; 1482 }, 1483 1484 /** 1485 * Returns HTML for the table title or empty string if title is null. 1486 */ 1487 getTitleHtml: function(title) 1488 { 1489 return title ? '<caption>' + title + '</caption>' : ''; 1490 }, 1491 1492 /** 1493 * Finds all matches in the source code. 1494 * @param {String} code Source code to process matches in. 1495 * @param {Array} matches Discovered regex matches. 1496 * @return {String} Returns formatted HTML with processed mathes. 1497 */ 1498 getMatchesHtml: function(code, matches) 1499 { 1500 var pos = 0, 1501 result = '', 1502 brushName = this.getParam('brush', '') 1503 ; 1504 1505 function getBrushNameCss(match) 1506 { 1507 var result = match ? (match.brushName || brushName) : brushName; 1508 return result ? result + ' ' : ''; 1509 }; 1510 1511 // Finally, go through the final list of matches and pull the all 1512 // together adding everything in between that isn't a match. 1513 for (var i = 0; i < matches.length; i++) 1514 { 1515 var match = matches[i], 1516 matchBrushName 1517 ; 1518 1519 if (match === null || match.length === 0) 1520 continue; 1521 1522 matchBrushName = getBrushNameCss(match); 1523 1524 result += wrapLinesWithCode(code.substr(pos, match.index - pos), matchBrushName + 'plain') 1525 + wrapLinesWithCode(match.value, matchBrushName + match.css) 1526 ; 1527 1528 pos = match.index + match.length + (match.offset || 0); 1529 } 1530 1531 // don't forget to add whatever's remaining in the string 1532 result += wrapLinesWithCode(code.substr(pos), getBrushNameCss() + 'plain'); 1533 1534 return result; 1535 }, 1536 1537 /** 1538 * Generates HTML markup for the whole syntax highlighter. 1539 * @param {String} code Source code. 1540 * @return {String} Returns HTML markup. 1541 */ 1542 getHtml: function(code) 1543 { 1544 var html = '', 1545 classes = [ 'syntaxhighlighter' ], 1546 tabSize, 1547 matches, 1548 lineNumbers 1549 ; 1550 1551 // process light mode 1552 if (this.getParam('light') == true) 1553 this.params.toolbar = this.params.gutter = false; 1554 1555 className = 'syntaxhighlighter'; 1556 1557 if (this.getParam('collapse') == true) 1558 classes.push('collapsed'); 1559 1560 if ((gutter = this.getParam('gutter')) == false) 1561 classes.push('nogutter'); 1562 1563 // add custom user style name 1564 classes.push(this.getParam('class-name')); 1565 1566 // add brush alias to the class name for custom CSS 1567 classes.push(this.getParam('brush')); 1568 1569 code = trimFirstAndLastLines(code) 1570 .replace(/\r/g, ' ') // IE lets these buggers through 1571 ; 1572 1573 tabSize = this.getParam('tab-size'); 1574 1575 // replace tabs with spaces 1576 code = this.getParam('smart-tabs') == true 1577 ? processSmartTabs(code, tabSize) 1578 : processTabs(code, tabSize) 1579 ; 1580 1581 // unindent code by the common indentation 1582 code = unindent(code); 1583 1584 if (gutter) 1585 lineNumbers = this.figureOutLineNumbers(code); 1586 1587 // find matches in the code using brushes regex list 1588 matches = this.findMatches(this.regexList, code); 1589 // processes found matches into the html 1590 html = this.getMatchesHtml(code, matches); 1591 // finally, split all lines so that they wrap well 1592 html = this.getCodeLinesHtml(html, lineNumbers); 1593 1594 // finally, process the links 1595 if (this.getParam('auto-links')) 1596 html = processUrls(html); 1597 1598 html = 1599 '<div id="' + getHighlighterId(this.id) + '" class="' + classes.join(' ') + '">' 1600 + (this.getParam('toolbar') ? sh.toolbar.getHtml(this) : '') 1601 + '<table border="0" cellpadding="0" cellspacing="0">' 1602 + this.getTitleHtml(this.getParam('title')) 1603 + '<tbody>' 1604 + '<tr>' 1605 + (gutter ? '<td class="gutter">' + this.getLineNumbersHtml(code) + '</td>' : '') 1606 + '<td class="code">' 1607 + '<div class="container">' 1608 + html 1609 + '</div>' 1610 + '</td>' 1611 + '</tr>' 1612 + '</tbody>' 1613 + '</table>' 1614 + '</div>' 1615 ; 1616 1617 return html; 1618 }, 1619 1620 /** 1621 * Highlights the code and returns complete HTML. 1622 * @param {String} code Code to highlight. 1623 * @return {Element} Returns container DIV element with all markup. 1624 */ 1625 getDiv: function(code) 1626 { 1627 if (code === null) 1628 code = ''; 1629 1630 this.code = code; 1631 1632 var div = this.create('div'); 1633 1634 // create main HTML 1635 div.innerHTML = this.getHtml(code); 1636 1637 // set up click handlers 1638 if (this.getParam('toolbar')) 1639 attachEvent(findElement(div, '.toolbar'), 'click', sh.toolbar.handler); 1640 1641 if (this.getParam('quick-code')) 1642 attachEvent(findElement(div, '.code'), 'dblclick', this.quickCodeHandler); 1643 1644 return div; 1645 }, 1646 1647 /** 1648 * Initializes the highlighter/brush. 1649 * 1650 * Constructor isn't used for initialization so that nothing executes during necessary 1651 * `new SyntaxHighlighter.Highlighter()` call when setting up brush inheritence. 1652 * 1653 * @param {Hash} params Highlighter parameters. 1654 */ 1655 init: function(params) 1656 { 1657 this.id = guid(); 1658 1659 // register this instance in the highlighters list 1660 storeHighlighter(this); 1661 1662 // local params take precedence over defaults 1663 this.params = merge(sh.defaults, params || {}) 1664 1665 // process light mode 1666 if (this.getParam('light') == true) 1667 this.params.toolbar = this.params.gutter = false; 1668 }, 1669 1670 /** 1671 * Converts space separated list of keywords into a regular expression string. 1672 * @param {String} str Space separated keywords. 1673 * @return {String} Returns regular expression string. 1674 */ 1675 getKeywords: function(str) 1676 { 1677 str = str 1678 .replace(/^\s+|\s+$/g, '') 1679 .replace(/\s+/g, '|') 1680 ; 1681 1682 return '\\b(?:' + str + ')\\b'; 1683 }, 1684 1685 /** 1686 * Makes a brush compatible with the `html-script` functionality. 1687 * @param {Object} regexGroup Object containing `left` and `right` regular expressions. 1688 */ 1689 forHtmlScript: function(regexGroup) 1690 { 1691 this.htmlScript = { 1692 left : { regex: regexGroup.left, css: 'script' }, 1693 right : { regex: regexGroup.right, css: 'script' }, 1694 code : XRegExp( 1695 "(?<left>" + regexGroup.left.source + ")" + 1696 "(?<code>.*?)" + 1697 "(?<right>" + regexGroup.right.source + ")", 1698 "sgi" 1699 ) 1700 }; 1701 } 1702 }; // end of Highlighter 1703 1704 return sh; 1705 }(); // end of anonymous function 1706]]></script> 1707 1708</body> 1709</html> 1710