PageRenderTime 85ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/wojilu.Web/static/js/syntaxhighlighter/src/shCore.js

https://bitbucket.org/kingshine/wojilu
JavaScript | 2077 lines | 1731 code | 138 blank | 208 comment | 76 complexity | 414f18c14a441eaab26e6277d997915a MD5 | raw file
Possible License(s): MIT

Large files files are truncated, but you can click here to view the full file

  1. /**
  2. * SyntaxHighlighter
  3. * http://alexgorbatchev.com/
  4. *
  5. * SyntaxHighlighter is donationware. If you are using it, please donate.
  6. * http://alexgorbatchev.com/wiki/SyntaxHighlighter:Donate
  7. *
  8. * @version
  9. * 2.1.382 (June 24 2010)
  10. *
  11. * @copyright
  12. * Copyright (C) 2004-2009 Alex Gorbatchev.
  13. *
  14. * @license
  15. * This file is part of SyntaxHighlighter.
  16. *
  17. * SyntaxHighlighter is free software: you can redistribute it and/or modify
  18. * it under the terms of the GNU Lesser General Public License as published by
  19. * the Free Software Foundation, either version 3 of the License, or
  20. * (at your option) any later version.
  21. *
  22. * SyntaxHighlighter is distributed in the hope that it will be useful,
  23. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  24. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  25. * GNU General Public License for more details.
  26. *
  27. * You should have received a copy of the GNU General Public License
  28. * along with SyntaxHighlighter. If not, see <http://www.gnu.org/copyleft/lesser.html>.
  29. */
  30. //
  31. // Begin anonymous function. This is used to contain local scope variables without polutting global scope.
  32. //
  33. if (!window.SyntaxHighlighter) var SyntaxHighlighter = function() {
  34. // Shortcut object which will be assigned to the SyntaxHighlighter variable.
  35. // This is a shorthand for local reference in order to avoid long namespace
  36. // references to SyntaxHighlighter.whatever...
  37. var sh = {
  38. defaults : {
  39. /** Additional CSS class names to be added to highlighter elements. */
  40. 'class-name' : '',
  41. /** First line number. */
  42. 'first-line' : 1,
  43. /**
  44. * Pads line numbers. Possible values are:
  45. *
  46. * false - don't pad line numbers.
  47. * true - automaticaly pad numbers with minimum required number of leading zeroes.
  48. * [int] - length up to which pad line numbers.
  49. */
  50. 'pad-line-numbers' : true,
  51. /** Lines to highlight. */
  52. 'highlight' : null,
  53. /** Enables or disables smart tabs. */
  54. 'smart-tabs' : true,
  55. /** Gets or sets tab size. */
  56. 'tab-size' : 4,
  57. /** Enables or disables gutter. */
  58. 'gutter' : true,
  59. /** Enables or disables toolbar. */
  60. 'toolbar' : true,
  61. /** Forces code view to be collapsed. */
  62. 'collapse' : false,
  63. /** Enables or disables automatic links. */
  64. 'auto-links' : true,
  65. /** Gets or sets light mode. Equavalent to turning off gutter and toolbar. */
  66. 'light' : false,
  67. /** Enables or disables automatic line wrapping. */
  68. 'wrap-lines' : true,
  69. 'html-script' : false
  70. },
  71. config : {
  72. /** Enables use of <SCRIPT type="syntaxhighlighter" /> tags. */
  73. useScriptTags : true,
  74. /** Path to the copy to clipboard SWF file. */
  75. clipboardSwf : null,
  76. /** Width of an item in the toolbar. */
  77. toolbarItemWidth : 16,
  78. /** Height of an item in the toolbar. */
  79. toolbarItemHeight : 16,
  80. /** Blogger mode flag. */
  81. bloggerMode : false,
  82. stripBrs : false,
  83. /** Name of the tag that SyntaxHighlighter will automatically look for. */
  84. tagName : 'pre',
  85. strings : {
  86. expandSource : 'show source',
  87. viewSource : 'view source',
  88. copyToClipboard : 'copy to clipboard',
  89. copyToClipboardConfirmation : 'The code is in your clipboard now',
  90. print : 'print',
  91. help : '?',
  92. alert: 'SyntaxHighlighter\n\n',
  93. noBrush : 'Can\'t find brush for: ',
  94. brushNotHtmlScript : 'Brush wasn\'t configured for html-script option: ',
  95. // this is populated by the build script
  96. aboutDialog : '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>About SyntaxHighlighter</title></head><body style="font-family:Geneva,Arial,Helvetica,sans-serif;background-color:#fff;color:#000;font-size:1em;text-align:center;"><div style="text-align:center;margin-top:3em;"><div style="font-size:xx-large;">SyntaxHighlighter</div><div style="font-size:.75em;margin-bottom:4em;"><div>version 2.1.382 (June 24 2010)</div><div><a href="http://alexgorbatchev.com" target="_blank" style="color:#0099FF;text-decoration:none;">http://alexgorbatchev.com</a></div><div>If you like this script, please <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=2930402" style="color:#0099FF;text-decoration:none;">donate</a> to keep development active!</div></div><div>JavaScript code syntax highlighter.</div><div>Copyright 2004-2009 Alex Gorbatchev.</div></div></body></html>'
  97. },
  98. /** If true, output will show HTML produces instead. */
  99. debug : false
  100. },
  101. /** Internal 'global' variables. */
  102. vars : {
  103. discoveredBrushes : null,
  104. spaceWidth : null,
  105. printFrame : null,
  106. highlighters : {}
  107. },
  108. /** This object is populated by user included external brush files. */
  109. brushes : {},
  110. /** Common regular expressions. */
  111. regexLib : {
  112. multiLineCComments : /\/\*[\s\S]*?\*\//gm,
  113. singleLineCComments : /\/\/.*$/gm,
  114. singleLinePerlComments : /#.*$/gm,
  115. doubleQuotedString : /"([^\\"\n]|\\.)*"/g,
  116. singleQuotedString : /'([^\\'\n]|\\.)*'/g,
  117. multiLineDoubleQuotedString : /"([^\\"]|\\.)*"/g,
  118. multiLineSingleQuotedString : /'([^\\']|\\.)*'/g,
  119. xmlComments : /(&lt;|<)!--[\s\S]*?--(&gt;|>)/gm,
  120. url : /&lt;\w+:\/\/[\w-.\/?%&=@:;]*&gt;|\w+:\/\/[\w-.\/?%&=@:;]*/g,
  121. /** <?= ?> tags. */
  122. phpScriptTags : { left: /(&lt;|<)\?=?/g, right: /\?(&gt;|>)/g },
  123. /** <%= %> tags. */
  124. aspScriptTags : { left: /(&lt;|<)%=?/g, right: /%(&gt;|>)/g },
  125. /** <script></script> tags. */
  126. scriptScriptTags : { left: /(&lt;|<)\s*script.*?(&gt;|>)/gi, right: /(&lt;|<)\/\s*script\s*(&gt;|>)/gi }
  127. },
  128. toolbar : {
  129. /**
  130. * Creates new toolbar for a highlighter.
  131. * @param {Highlighter} highlighter Target highlighter.
  132. */
  133. create : function(highlighter)
  134. {
  135. var div = document.createElement('DIV'),
  136. items = sh.toolbar.items
  137. ;
  138. div.className = 'toolbar';
  139. for (var name in items)
  140. {
  141. var constructor = items[name],
  142. command = new constructor(highlighter),
  143. element = command.create()
  144. ;
  145. highlighter.toolbarCommands[name] = command;
  146. if (element == null)
  147. continue;
  148. if (typeof(element) == 'string')
  149. element = sh.toolbar.createButton(element, highlighter.id, name);
  150. element.className += 'item ' + name;
  151. div.appendChild(element);
  152. }
  153. return div;
  154. },
  155. /**
  156. * Create a standard anchor button for the toolbar.
  157. * @param {String} label Label text to display.
  158. * @param {String} highlighterId Highlighter ID that this button would belong to.
  159. * @param {String} commandName Command name that would be executed.
  160. * @return {Element} Returns an 'A' element.
  161. */
  162. createButton : function(label, highlighterId, commandName)
  163. {
  164. var a = document.createElement('a'),
  165. style = a.style,
  166. config = sh.config,
  167. width = config.toolbarItemWidth,
  168. height = config.toolbarItemHeight
  169. ;
  170. a.href = '#' + commandName;
  171. a.title = label;
  172. a.highlighterId = highlighterId;
  173. a.commandName = commandName;
  174. a.innerHTML = label;
  175. if (isNaN(width) == false)
  176. style.width = width + 'px';
  177. if (isNaN(height) == false)
  178. style.height = height + 'px';
  179. a.onclick = function(e)
  180. {
  181. try
  182. {
  183. sh.toolbar.executeCommand(
  184. this,
  185. e || window.event,
  186. this.highlighterId,
  187. this.commandName
  188. );
  189. }
  190. catch(e)
  191. {
  192. sh.utils.alert(e.message);
  193. }
  194. return false;
  195. };
  196. return a;
  197. },
  198. /**
  199. * Executes a toolbar command.
  200. * @param {Element} sender Sender element.
  201. * @param {MouseEvent} event Original mouse event object.
  202. * @param {String} highlighterId Highlighter DIV element ID.
  203. * @param {String} commandName Name of the command to execute.
  204. * @return {Object} Passes out return value from command execution.
  205. */
  206. executeCommand : function(sender, event, highlighterId, commandName, args)
  207. {
  208. var highlighter = sh.vars.highlighters[highlighterId],
  209. command
  210. ;
  211. if (highlighter == null || (command = highlighter.toolbarCommands[commandName]) == null)
  212. return null;
  213. return command.execute(sender, event, args);
  214. },
  215. /** Collection of toolbar items. */
  216. items : {
  217. expandSource : function(highlighter)
  218. {
  219. this.create = function()
  220. {
  221. if (highlighter.getParam('collapse') != true)
  222. return;
  223. return sh.config.strings.expandSource;
  224. };
  225. this.execute = function(sender, event, args)
  226. {
  227. var div = highlighter.div;
  228. sender.parentNode.removeChild(sender);
  229. div.className = div.className.replace('collapsed', '');
  230. };
  231. },
  232. /**
  233. * Command to open a new window and display the original unformatted source code inside.
  234. */
  235. viewSource : function(highlighter)
  236. {
  237. this.create = function()
  238. {
  239. return sh.config.strings.viewSource;
  240. };
  241. this.execute = function(sender, event, args)
  242. {
  243. var code = sh.utils.fixInputString(highlighter.originalCode).replace(/</g, '&lt;'),
  244. wnd = sh.utils.popup('', '_blank', 750, 400, 'location=0, resizable=1, menubar=0, scrollbars=1')
  245. ;
  246. code = sh.utils.unindent(code);
  247. wnd.document.write('<pre>' + code + '</pre>');
  248. wnd.document.close();
  249. };
  250. },
  251. /**
  252. * Command to copy the original source code in to the clipboard.
  253. * Uses Flash method if <code>clipboardSwf</code> is configured.
  254. */
  255. copyToClipboard : function(highlighter)
  256. {
  257. var flashDiv, flashSwf,
  258. highlighterId = highlighter.id
  259. ;
  260. this.create = function()
  261. {
  262. var config = sh.config;
  263. // disable functionality if running locally
  264. if (config.clipboardSwf == null)
  265. return null;
  266. function params(list)
  267. {
  268. var result = '';
  269. for (var name in list)
  270. result += "<param name='" + name + "' value='" + list[name] + "'/>";
  271. return result;
  272. };
  273. function attributes(list)
  274. {
  275. var result = '';
  276. for (var name in list)
  277. result += " " + name + "='" + list[name] + "'";
  278. return result;
  279. };
  280. var args1 = {
  281. width : config.toolbarItemWidth,
  282. height : config.toolbarItemHeight,
  283. id : highlighterId + '_clipboard',
  284. type : 'application/x-shockwave-flash',
  285. title : sh.config.strings.copyToClipboard
  286. },
  287. // these arguments are used in IE's <param /> collection
  288. args2 = {
  289. allowScriptAccess : 'always',
  290. wmode : 'transparent',
  291. flashVars : 'highlighterId=' + highlighterId,
  292. menu : 'false'
  293. },
  294. swf = config.clipboardSwf,
  295. html
  296. ;
  297. if (/msie/i.test(navigator.userAgent))
  298. {
  299. html = '<object'
  300. + attributes({
  301. classid : 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000',
  302. codebase : 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0'
  303. })
  304. + attributes(args1)
  305. + '>'
  306. + params(args2)
  307. + params({ movie : swf })
  308. + '</object>'
  309. ;
  310. }
  311. else
  312. {
  313. html = '<embed'
  314. + attributes(args1)
  315. + attributes(args2)
  316. + attributes({ src : swf })
  317. + '/>'
  318. ;
  319. }
  320. flashDiv = document.createElement('div');
  321. flashDiv.innerHTML = html;
  322. return flashDiv;
  323. };
  324. this.execute = function(sender, event, args)
  325. {
  326. var command = args.command;
  327. switch (command)
  328. {
  329. case 'get':
  330. var code = sh.utils.unindent(
  331. sh.utils.fixInputString(highlighter.originalCode)
  332. .replace(/&lt;/g, '<')
  333. .replace(/&gt;/g, '>')
  334. .replace(/&amp;/g, '&')
  335. );
  336. if(window.clipboardData)
  337. // will fall through to the confirmation because there isn't a break
  338. window.clipboardData.setData('text', code);
  339. else
  340. return sh.utils.unindent(code);
  341. case 'ok':
  342. sh.utils.alert(sh.config.strings.copyToClipboardConfirmation);
  343. break;
  344. case 'error':
  345. sh.utils.alert(args.message);
  346. break;
  347. }
  348. };
  349. },
  350. /** Command to print the colored source code. */
  351. printSource : function(highlighter)
  352. {
  353. this.create = function()
  354. {
  355. return sh.config.strings.print;
  356. };
  357. this.execute = function(sender, event, args)
  358. {
  359. var iframe = document.createElement('IFRAME'),
  360. doc = null
  361. ;
  362. // make sure there is never more than one hidden iframe created by SH
  363. if (sh.vars.printFrame != null)
  364. document.body.removeChild(sh.vars.printFrame);
  365. sh.vars.printFrame = iframe;
  366. // this hides the iframe
  367. iframe.style.cssText = 'position:absolute;width:0px;height:0px;left:-500px;top:-500px;';
  368. document.body.appendChild(iframe);
  369. doc = iframe.contentWindow.document;
  370. copyStyles(doc, window.document);
  371. doc.write('<div class="' + highlighter.div.className.replace('collapsed', '') + ' printing">' + highlighter.div.innerHTML + '</div>');
  372. doc.close();
  373. iframe.contentWindow.focus();
  374. iframe.contentWindow.print();
  375. function copyStyles(destDoc, sourceDoc)
  376. {
  377. var links = sourceDoc.getElementsByTagName('link');
  378. for(var i = 0; i < links.length; i++)
  379. if(links[i].rel.toLowerCase() == 'stylesheet' && /shCore\.css$/.test(links[i].href))
  380. destDoc.write('<link type="text/css" rel="stylesheet" href="' + links[i].href + '"></link>');
  381. };
  382. };
  383. },
  384. /** Command to display the about dialog window. */
  385. about : function(highlighter)
  386. {
  387. this.create = function()
  388. {
  389. return sh.config.strings.help;
  390. };
  391. this.execute = function(sender, event)
  392. {
  393. var wnd = sh.utils.popup('', '_blank', 500, 250, 'scrollbars=0'),
  394. doc = wnd.document
  395. ;
  396. doc.write(sh.config.strings.aboutDialog);
  397. doc.close();
  398. wnd.focus();
  399. };
  400. }
  401. }
  402. },
  403. utils : {
  404. /**
  405. * Finds an index of element in the array.
  406. * @ignore
  407. * @param {Object} searchElement
  408. * @param {Number} fromIndex
  409. * @return {Number} Returns index of element if found; -1 otherwise.
  410. */
  411. indexOf : function(array, searchElement, fromIndex)
  412. {
  413. fromIndex = Math.max(fromIndex || 0, 0);
  414. for (var i = fromIndex; i < array.length; i++)
  415. if(array[i] == searchElement)
  416. return i;
  417. return -1;
  418. },
  419. /**
  420. * Generates a unique element ID.
  421. */
  422. guid : function(prefix)
  423. {
  424. return prefix + Math.round(Math.random() * 1000000).toString();
  425. },
  426. /**
  427. * Merges two objects. Values from obj2 override values in obj1.
  428. * Function is NOT recursive and works only for one dimensional objects.
  429. * @param {Object} obj1 First object.
  430. * @param {Object} obj2 Second object.
  431. * @return {Object} Returns combination of both objects.
  432. */
  433. merge: function(obj1, obj2)
  434. {
  435. var result = {}, name;
  436. for (name in obj1)
  437. result[name] = obj1[name];
  438. for (name in obj2)
  439. result[name] = obj2[name];
  440. return result;
  441. },
  442. /**
  443. * Attempts to convert string to boolean.
  444. * @param {String} value Input string.
  445. * @return {Boolean} Returns true if input was "true", false if input was "false" and value otherwise.
  446. */
  447. toBoolean: function(value)
  448. {
  449. switch (value)
  450. {
  451. case "true":
  452. return true;
  453. case "false":
  454. return false;
  455. }
  456. return value;
  457. },
  458. /**
  459. * Opens up a centered popup window.
  460. * @param {String} url URL to open in the window.
  461. * @param {String} name Popup name.
  462. * @param {int} width Popup width.
  463. * @param {int} height Popup height.
  464. * @param {String} options window.open() options.
  465. * @return {Window} Returns window instance.
  466. */
  467. popup: function(url, name, width, height, options)
  468. {
  469. var x = (screen.width - width) / 2,
  470. y = (screen.height - height) / 2
  471. ;
  472. options += ', left=' + x +
  473. ', top=' + y +
  474. ', width=' + width +
  475. ', height=' + height
  476. ;
  477. options = options.replace(/^,/, '');
  478. var win = window.open(url, name, options);
  479. win.focus();
  480. return win;
  481. },
  482. /**
  483. * Adds event handler to the target object.
  484. * @param {Object} obj Target object.
  485. * @param {String} type Name of the event.
  486. * @param {Function} func Handling function.
  487. */
  488. addEvent: function(obj, type, func)
  489. {
  490. if (obj.attachEvent)
  491. {
  492. obj['e' + type + func] = func;
  493. obj[type + func] = function()
  494. {
  495. obj['e' + type + func](window.event);
  496. }
  497. obj.attachEvent('on' + type, obj[type + func]);
  498. }
  499. else
  500. {
  501. obj.addEventListener(type, func, false);
  502. }
  503. },
  504. /**
  505. * Displays an alert.
  506. * @param {String} str String to display.
  507. */
  508. alert: function(str)
  509. {
  510. alert(sh.config.strings.alert + str)
  511. },
  512. /**
  513. * Finds a brush by its alias.
  514. *
  515. * @param {String} alias Brush alias.
  516. * @param {Boolean} alert Suppresses the alert if false.
  517. * @return {Brush} Returns bursh constructor if found, null otherwise.
  518. */
  519. findBrush: function(alias, alert)
  520. {
  521. var brushes = sh.vars.discoveredBrushes,
  522. result = null
  523. ;
  524. if (brushes == null)
  525. {
  526. brushes = {};
  527. // Find all brushes
  528. for (var brush in sh.brushes)
  529. {
  530. var aliases = sh.brushes[brush].aliases;
  531. if (aliases == null)
  532. continue;
  533. // keep the brush name
  534. sh.brushes[brush].name = brush.toLowerCase();
  535. for (var i = 0; i < aliases.length; i++)
  536. brushes[aliases[i]] = brush;
  537. }
  538. sh.vars.discoveredBrushes = brushes;
  539. }
  540. result = sh.brushes[brushes[alias]];
  541. if (result == null && alert != false)
  542. sh.utils.alert(sh.config.strings.noBrush + alias);
  543. return result;
  544. },
  545. /**
  546. * Executes a callback on each line and replaces each line with result from the callback.
  547. * @param {Object} str Input string.
  548. * @param {Object} callback Callback function taking one string argument and returning a string.
  549. */
  550. eachLine: function(str, callback)
  551. {
  552. var lines = str.split('\n');
  553. for (var i = 0; i < lines.length; i++)
  554. lines[i] = callback(lines[i]);
  555. return lines.join('\n');
  556. },
  557. /**
  558. * This is a special trim which only removes first and last empty lines
  559. * and doesn't affect valid leading space on the first line.
  560. *
  561. * @param {String} str Input string
  562. * @return {String} Returns string without empty first and last lines.
  563. */
  564. trimFirstAndLastLines: function(str)
  565. {
  566. return str.replace(/^[ ]*[\n]+|[\n]*[ ]*$/g, '');
  567. },
  568. /**
  569. * Parses key/value pairs into hash object.
  570. *
  571. * Understands the following formats:
  572. * - name: word;
  573. * - name: [word, word];
  574. * - name: "string";
  575. * - name: 'string';
  576. *
  577. * For example:
  578. * name1: value; name2: [value, value]; name3: 'value'
  579. *
  580. * @param {String} str Input string.
  581. * @return {Object} Returns deserialized object.
  582. */
  583. parseParams: function(str)
  584. {
  585. var match,
  586. result = {},
  587. arrayRegex = new XRegExp("^\\[(?<values>(.*?))\\]$"),
  588. regex = new XRegExp(
  589. "(?<name>[\\w-]+)" +
  590. "\\s*:\\s*" +
  591. "(?<value>" +
  592. "[\\w-%#]+|" + // word
  593. "\\[.*?\\]|" + // [] array
  594. '".*?"|' + // "" string
  595. "'.*?'" + // '' string
  596. ")\\s*;?",
  597. "g"
  598. )
  599. ;
  600. while ((match = regex.exec(str)) != null)
  601. {
  602. var value = match.value
  603. .replace(/^['"]|['"]$/g, '') // strip quotes from end of strings
  604. ;
  605. // try to parse array value
  606. if (value != null && arrayRegex.test(value))
  607. {
  608. var m = arrayRegex.exec(value);
  609. value = m.values.length > 0 ? m.values.split(/\s*,\s*/) : [];
  610. }
  611. result[match.name] = value;
  612. }
  613. return result;
  614. },
  615. /**
  616. * Wraps each line of the string into <code/> tag with given style applied to it.
  617. *
  618. * @param {String} str Input string.
  619. * @param {String} css Style name to apply to the string.
  620. * @return {String} Returns input string with each line surrounded by <span/> tag.
  621. */
  622. decorate: function(str, css)
  623. {
  624. if (str == null || str.length == 0 || str == '\n')
  625. return str;
  626. str = str.replace(/</g, '&lt;');
  627. // Replace two or more sequential spaces with &nbsp; leaving last space untouched.
  628. str = str.replace(/ {2,}/g, function(m)
  629. {
  630. var spaces = '';
  631. for (var i = 0; i < m.length - 1; i++)
  632. spaces += '&nbsp;';
  633. return spaces + ' ';
  634. });
  635. // Split each line and apply <span class="...">...</span> to them so that
  636. // leading spaces aren't included.
  637. if (css != null)
  638. str = sh.utils.eachLine(str, function(line)
  639. {
  640. if (line.length == 0)
  641. return '';
  642. var spaces = '';
  643. line = line.replace(/^(&nbsp;| )+/, function(s)
  644. {
  645. spaces = s;
  646. return '';
  647. });
  648. if (line.length == 0)
  649. return spaces;
  650. return spaces + '<code class="' + css + '">' + line + '</code>';
  651. });
  652. return str;
  653. },
  654. /**
  655. * Pads number with zeros until it's length is the same as given length.
  656. *
  657. * @param {Number} number Number to pad.
  658. * @param {Number} length Max string length with.
  659. * @return {String} Returns a string padded with proper amount of '0'.
  660. */
  661. padNumber : function(number, length)
  662. {
  663. var result = number.toString();
  664. while (result.length < length)
  665. result = '0' + result;
  666. return result;
  667. },
  668. /**
  669. * Measures width of a single space character.
  670. * @return {Number} Returns width of a single space character.
  671. */
  672. measureSpace : function()
  673. {
  674. var container = document.createElement('div'),
  675. span,
  676. result = 0,
  677. body = document.body,
  678. id = sh.utils.guid('measureSpace'),
  679. // variable names will be compressed, so it's better than a plain string
  680. divOpen = '<div class="',
  681. closeDiv = '</div>',
  682. closeSpan = '</span>'
  683. ;
  684. // we have to duplicate highlighter nested structure in order to get an acurate space measurment
  685. container.innerHTML =
  686. divOpen + 'syntaxhighlighter">'
  687. + divOpen + 'lines">'
  688. + divOpen + 'line">'
  689. + divOpen + 'content'
  690. + '"><span class="block"><span id="' + id + '">&nbsp;' + closeSpan + closeSpan
  691. + closeDiv
  692. + closeDiv
  693. + closeDiv
  694. + closeDiv
  695. ;
  696. body.appendChild(container);
  697. span = document.getElementById(id);
  698. if (/opera/i.test(navigator.userAgent))
  699. {
  700. var style = window.getComputedStyle(span, null);
  701. result = parseInt(style.getPropertyValue("width"));
  702. }
  703. else
  704. {
  705. result = span.offsetWidth;
  706. }
  707. body.removeChild(container);
  708. return result;
  709. },
  710. /**
  711. * Replaces tabs with spaces.
  712. *
  713. * @param {String} code Source code.
  714. * @param {Number} tabSize Size of the tab.
  715. * @return {String} Returns code with all tabs replaces by spaces.
  716. */
  717. processTabs : function(code, tabSize)
  718. {
  719. var tab = '';
  720. for (var i = 0; i < tabSize; i++)
  721. tab += ' ';
  722. return code.replace(/\t/g, tab);
  723. },
  724. /**
  725. * Replaces tabs with smart spaces.
  726. *
  727. * @param {String} code Code to fix the tabs in.
  728. * @param {Number} tabSize Number of spaces in a column.
  729. * @return {String} Returns code with all tabs replaces with roper amount of spaces.
  730. */
  731. processSmartTabs : function(code, tabSize)
  732. {
  733. var lines = code.split('\n'),
  734. tab = '\t',
  735. spaces = ''
  736. ;
  737. // Create a string with 1000 spaces to copy spaces from...
  738. // It's assumed that there would be no indentation longer than that.
  739. for (var i = 0; i < 50; i++)
  740. spaces += ' '; // 20 spaces * 50
  741. // This function inserts specified amount of spaces in the string
  742. // where a tab is while removing that given tab.
  743. function insertSpaces(line, pos, count)
  744. {
  745. return line.substr(0, pos)
  746. + spaces.substr(0, count)
  747. + line.substr(pos + 1, line.length) // pos + 1 will get rid of the tab
  748. ;
  749. };
  750. // Go through all the lines and do the 'smart tabs' magic.
  751. code = sh.utils.eachLine(code, function(line)
  752. {
  753. if (line.indexOf(tab) == -1)
  754. return line;
  755. var pos = 0;
  756. while ((pos = line.indexOf(tab)) != -1)
  757. {
  758. // This is pretty much all there is to the 'smart tabs' logic.
  759. // Based on the position within the line and size of a tab,
  760. // calculate the amount of spaces we need to insert.
  761. var spaces = tabSize - pos % tabSize;
  762. line = insertSpaces(line, pos, spaces);
  763. }
  764. return line;
  765. });
  766. return code;
  767. },
  768. /**
  769. * Performs various string fixes based on configuration.
  770. */
  771. fixInputString : function(str)
  772. {
  773. var br = /<br\s*\/?>|&lt;br\s*\/?&gt;/gi;
  774. if (sh.config.bloggerMode == true)
  775. str = str.replace(br, '\n');
  776. if (sh.config.stripBrs == true)
  777. str = str.replace(br, '');
  778. return str;
  779. },
  780. /**
  781. * Removes all white space at the begining and end of a string.
  782. *
  783. * @param {String} str String to trim.
  784. * @return {String} Returns string without leading and following white space characters.
  785. */
  786. trim: function(str)
  787. {
  788. return str.replace(/^\s+|\s+$/g, '');
  789. },
  790. /**
  791. * Unindents a block of text by the lowest common indent amount.
  792. * @param {String} str Text to unindent.
  793. * @return {String} Returns unindented text block.
  794. */
  795. unindent: function(str)
  796. {
  797. var lines = sh.utils.fixInputString(str).split('\n'),
  798. indents = new Array(),
  799. regex = /^\s*/,
  800. min = 1000
  801. ;
  802. // go through every line and check for common number of indents
  803. for (var i = 0; i < lines.length && min > 0; i++)
  804. {
  805. var line = lines[i];
  806. if (sh.utils.trim(line).length == 0)
  807. continue;
  808. var matches = regex.exec(line);
  809. // In the event that just one line doesn't have leading white space
  810. // we can't unindent anything, so bail completely.
  811. if (matches == null)
  812. return str;
  813. min = Math.min(matches[0].length, min);
  814. }
  815. // trim minimum common number of white space from the begining of every line
  816. if (min > 0)
  817. for (var i = 0; i < lines.length; i++)
  818. lines[i] = lines[i].substr(min);
  819. return lines.join('\n');
  820. },
  821. /**
  822. * Callback method for Array.sort() which sorts matches by
  823. * index position and then by length.
  824. *
  825. * @param {Match} m1 Left object.
  826. * @param {Match} m2 Right object.
  827. * @return {Number} Returns -1, 0 or -1 as a comparison result.
  828. */
  829. matchesSortCallback: function(m1, m2)
  830. {
  831. // sort matches by index first
  832. if(m1.index < m2.index)
  833. return -1;
  834. else if(m1.index > m2.index)
  835. return 1;
  836. else
  837. {
  838. // if index is the same, sort by length
  839. if(m1.length < m2.length)
  840. return -1;
  841. else if(m1.length > m2.length)
  842. return 1;
  843. }
  844. return 0;
  845. },
  846. /**
  847. * Executes given regular expression on provided code and returns all
  848. * matches that are found.
  849. *
  850. * @param {String} code Code to execute regular expression on.
  851. * @param {Object} regex Regular expression item info from <code>regexList</code> collection.
  852. * @return {Array} Returns a list of Match objects.
  853. */
  854. getMatches: function(code, regexInfo)
  855. {
  856. function defaultAdd(match, regexInfo)
  857. {
  858. return [new sh.Match(match[0], match.index, regexInfo.css)];
  859. };
  860. var index = 0,
  861. match = null,
  862. result = [],
  863. func = regexInfo.func ? regexInfo.func : defaultAdd
  864. ;
  865. while((match = regexInfo.regex.exec(code)) != null)
  866. result = result.concat(func(match, regexInfo));
  867. return result;
  868. },
  869. processUrls: function(code)
  870. {
  871. var lt = '&lt;',
  872. gt = '&gt;'
  873. ;
  874. return code.replace(sh.regexLib.url, function(m)
  875. {
  876. var suffix = '', prefix = '';
  877. // We include &lt; and &gt; in the URL for the common cases like <http://google.com>
  878. // The problem is that they get transformed into &lt;http://google.com&gt;
  879. // Where as &gt; easily looks like part of the URL string.
  880. if (m.indexOf(lt) == 0)
  881. {
  882. prefix = lt;
  883. m = m.substring(lt.length);
  884. }
  885. if (m.indexOf(gt) == m.length - gt.length)
  886. {
  887. m = m.substring(0, m.length - gt.length);
  888. suffix = gt;
  889. }
  890. return prefix + '<a href="' + m + '">' + m + '</a>' + suffix;
  891. });
  892. },
  893. /**
  894. * Finds all <SCRIPT TYPE="syntaxhighlighter" /> elements.
  895. * @return {Array} Returns array of all found SyntaxHighlighter tags.
  896. */
  897. getSyntaxHighlighterScriptTags: function()
  898. {
  899. var tags = document.getElementsByTagName('script'),
  900. result = []
  901. ;
  902. for (var i = 0; i < tags.length; i++)
  903. if (tags[i].type == 'syntaxhighlighter')
  904. result.push(tags[i]);
  905. return result;
  906. },
  907. /**
  908. * Strips <![CDATA[]]> from <SCRIPT /> content because it should be used
  909. * there in most cases for XHTML compliance.
  910. * @param {String} original Input code.
  911. * @return {String} Returns code without leading <![CDATA[]]> tags.
  912. */
  913. stripCData: function(original)
  914. {
  915. var left = '<![CDATA[',
  916. right = ']]>',
  917. // for some reason IE inserts some leading blanks here
  918. copy = sh.utils.trim(original),
  919. changed = false
  920. ;
  921. if (copy.indexOf(left) == 0)
  922. {
  923. copy = copy.substring(left.length);
  924. changed = true;
  925. }
  926. if (copy.indexOf(right) == copy.length - right.length)
  927. {
  928. copy = copy.substring(0, copy.length - right.length);
  929. changed = true;
  930. }
  931. return changed ? copy : original;
  932. }
  933. }, // end of utils
  934. /**
  935. * Shorthand to highlight all elements on the page that are marked as
  936. * SyntaxHighlighter source code.
  937. *
  938. * @param {Object} globalParams Optional parameters which override element's
  939. * parameters. Only used if element is specified.
  940. *
  941. * @param {Object} element Optional element to highlight. If none is
  942. * provided, all elements in the current document
  943. * are highlighted.
  944. */
  945. highlight : function(globalParams, element)
  946. {
  947. function toArray(source)
  948. {
  949. var result = [];
  950. for (var i = 0; i < source.length; i++)
  951. result.push(source[i]);
  952. return result;
  953. };
  954. var elements = element ? [element] : toArray(document.getElementsByTagName(sh.config.tagName)),
  955. propertyName = 'innerHTML',
  956. highlighter = null,
  957. conf = sh.config
  958. ;
  959. // support for <SCRIPT TYPE="syntaxhighlighter" /> feature
  960. if (conf.useScriptTags)
  961. elements = elements.concat(sh.utils.getSyntaxHighlighterScriptTags());
  962. if (elements.length === 0)
  963. return;
  964. for (var i = 0; i < elements.length; i++)
  965. {
  966. var target = elements[i],
  967. params = sh.utils.parseParams(target.className),
  968. brushName,
  969. code,
  970. result
  971. ;
  972. // local params take precedence over globals
  973. params = sh.utils.merge(globalParams, params);
  974. brushName = params['brush'];
  975. if (brushName == null)
  976. continue;
  977. // Instantiate a brush
  978. if (params['html-script'] == 'true' || sh.defaults['html-script'] == true)
  979. {
  980. highlighter = new sh.HtmlScript(brushName);
  981. brushName = 'htmlscript';
  982. }
  983. else
  984. {
  985. var brush = sh.utils.findBrush(brushName);
  986. if (brush)
  987. {
  988. brushName = brush.name;
  989. highlighter = new brush();
  990. }
  991. else
  992. {
  993. continue;
  994. }
  995. }
  996. code = target[propertyName];
  997. // remove CDATA from <SCRIPT/> tags if it's present
  998. if (conf.useScriptTags)
  999. code = sh.utils.stripCData(code);
  1000. params['brush-name'] = brushName;
  1001. highlighter.highlight(code, params);
  1002. result = highlighter.div;
  1003. if (sh.config.debug)
  1004. {
  1005. result = document.createElement('textarea');
  1006. result.value = highlighter.div.innerHTML;
  1007. result.style.width = '70em';
  1008. result.style.height = '30em';
  1009. }
  1010. target.parentNode.replaceChild(result, target);
  1011. }
  1012. },
  1013. /**
  1014. * Main entry point for the SyntaxHighlighter.
  1015. * @param {Object} params Optional params to apply to all highlighted elements.
  1016. */
  1017. all : function(params)
  1018. {
  1019. sh.utils.addEvent(
  1020. window,
  1021. 'load',
  1022. function() { sh.highlight(params); }
  1023. );
  1024. }
  1025. }; // end of sh
  1026. /**
  1027. * Match object.
  1028. */
  1029. sh.Match = function(value, index, css)
  1030. {
  1031. this.value = value;
  1032. this.index = index;
  1033. this.length = value.length;
  1034. this.css = css;
  1035. this.brushName = null;
  1036. };
  1037. sh.Match.prototype.toString = function()
  1038. {
  1039. return this.value;
  1040. };
  1041. /**
  1042. * Simulates HTML code with a scripting language embedded.
  1043. *
  1044. * @param {String} scriptBrushName Brush name of the scripting language.
  1045. */
  1046. sh.HtmlScript = function(scriptBrushName)
  1047. {
  1048. var brushClass = sh.utils.findBrush(scriptBrushName),
  1049. scriptBrush,
  1050. xmlBrush = new sh.brushes.Xml(),
  1051. bracketsRegex = null
  1052. ;
  1053. if (brushClass == null)
  1054. return;
  1055. scriptBrush = new brushClass();
  1056. this.xmlBrush = xmlBrush;
  1057. if (scriptBrush.htmlScript == null)
  1058. {
  1059. sh.utils.alert(sh.config.strings.brushNotHtmlScript + scriptBrushName);
  1060. return;
  1061. }
  1062. xmlBrush.regexList.push(
  1063. { regex: scriptBrush.htmlScript.code, func: process }
  1064. );
  1065. function offsetMatches(matches, offset)
  1066. {
  1067. for (var j = 0; j < matches.length; j++)
  1068. matches[j].index += offset;
  1069. }
  1070. function process(match, info)
  1071. {
  1072. var code = match.code,
  1073. matches = [],
  1074. regexList = scriptBrush.regexList,
  1075. offset = match.index + match.left.length,
  1076. htmlScript = scriptBrush.htmlScript,
  1077. result
  1078. ;
  1079. // add all matches from the code
  1080. for (var i = 0; i < regexList.length; i++)
  1081. {
  1082. result = sh.utils.getMatches(code, regexList[i]);
  1083. offsetMatches(result, offset);
  1084. matches = matches.concat(result);
  1085. }
  1086. // add left script bracket
  1087. if (htmlScript.left != null && match.left != null)
  1088. {
  1089. result = sh.utils.getMatches(match.left, htmlScript.left);
  1090. offsetMatches(result, match.index);
  1091. matches = matches.concat(result);
  1092. }
  1093. // add right script bracket
  1094. if (htmlScript.right != null && match.right != null)
  1095. {
  1096. result = sh.utils.getMatches(match.right, htmlScript.right);
  1097. offsetMatches(result, match.index + match[0].lastIndexOf(match.right));
  1098. matches = matches.concat(result);
  1099. }
  1100. for (var j = 0; j < matches.length; j++)
  1101. matches[j].brushName = brushClass.name;
  1102. return matches;
  1103. }
  1104. };
  1105. sh.HtmlScript.prototype.highlight = function(code, params)
  1106. {
  1107. this.xmlBrush.highlight(code, params);
  1108. this.div = this.xmlBrush.div;
  1109. }
  1110. /**
  1111. * Main Highlither class.
  1112. * @constructor
  1113. */
  1114. sh.Highlighter = function()
  1115. {
  1116. };
  1117. sh.Highlighter.prototype = {
  1118. /**
  1119. * Returns value of the parameter passed to the highlighter.
  1120. * @param {String} name Name of the parameter.
  1121. * @param {Object} defaultValue Default value.
  1122. * @return {Object} Returns found value or default value otherwise.
  1123. */
  1124. getParam : function(name, defaultValue)
  1125. {
  1126. var result = this.params[name];
  1127. return sh.utils.toBoolean(result == null ? defaultValue : result);
  1128. },
  1129. /**
  1130. * Shortcut to document.createElement().
  1131. * @param {String} name Name of the element to create (DIV, A, etc).
  1132. * @return {HTMLElement} Returns new HTML element.
  1133. */
  1134. create: function(name)
  1135. {
  1136. return document.createElement(name);
  1137. },
  1138. /**
  1139. * Applies all regular expression to the code and stores all found
  1140. * matches in the `this.matches` array.
  1141. * @param {Array} regexList List of regular expressions.
  1142. * @param {String} code Source code.
  1143. * @return {Array} Returns list of matches.
  1144. */
  1145. findMatches: function(regexList, code)
  1146. {
  1147. var result = [];
  1148. if (regexList != null)
  1149. for (var i = 0; i < regexList.length; i++)
  1150. // BUG: length returns len+1 for array if methods added to prototype chain (oising@gmail.com)
  1151. if (typeof (regexList[i]) == "object")
  1152. result = result.concat(sh.utils.getMatches(code, regexList[i]));
  1153. // sort the matches
  1154. return result.sort(sh.utils.matchesSortCallback);
  1155. },
  1156. /**
  1157. * Checks to see if any of the matches are inside of other matches.
  1158. * This process would get rid of highligted strings inside comments,
  1159. * keywords inside strings and so on.
  1160. */
  1161. removeNestedMatches: function()
  1162. {
  1163. var matches = this.matches;
  1164. // Optimized by Jose Prado (http://joseprado.com)
  1165. for (var i = 0; i < matches.length; i++)
  1166. {
  1167. if (matches[i] === null)
  1168. continue;
  1169. var itemI = matches[i],
  1170. itemIEndPos = itemI.index + itemI.length
  1171. ;
  1172. for (var j = i + 1; j < matches.length && matches[i] !== null; j++)
  1173. {
  1174. var itemJ = matches[j];
  1175. if (itemJ === null)
  1176. continue;
  1177. else if (itemJ.index > itemIEndPos)
  1178. break;
  1179. else if (itemJ.index == itemI.index && itemJ.length > itemI.length)
  1180. this.matches[i] = null;
  1181. else if (itemJ.index >= itemI.index && itemJ.index < itemIEndPos)
  1182. this.matches[j] = null;
  1183. }
  1184. }
  1185. },
  1186. /**
  1187. * Splits block of text into individual DIV lines.
  1188. * @param {String} code Code to highlight.
  1189. * @return {String} Returns highlighted code in HTML form.
  1190. */
  1191. createDisplayLines : function(code)
  1192. {
  1193. var lines = code.split('\n'),
  1194. firstLine = parseInt(this.getParam('first-line')),
  1195. padLength = this.getParam('pad-line-numbers'),
  1196. highlightedLines = this.getParam('highlight', []),
  1197. hasGutter = this.getParam('gutter')
  1198. ;
  1199. code = '';
  1200. if (padLength == true)
  1201. padLength = (firstLine + lines.length - 1).toString().length;
  1202. else if (isNaN(padLength) == true)
  1203. padLength = 0;
  1204. for (var i = 0; i < lines.length; i++)
  1205. {
  1206. var line = lines[i],
  1207. indent = /^(&nbsp;|\s)+/.exec(line),
  1208. lineClass = 'alt' + (i % 2 == 0 ? 1 : 2),
  1209. lineNumber = sh.utils.padNumber(firstLine + i, padLength),
  1210. highlighted = sh.utils.indexOf(highlightedLines, (firstLine + i).toString()) != -1,
  1211. spaces = null
  1212. ;
  1213. if (indent != null)
  1214. {
  1215. spaces = indent[0].toString();
  1216. line = line.substr(spaces.length);
  1217. }
  1218. line = sh.utils.trim(line);
  1219. if (line.length == 0)
  1220. line = '&nbsp;';
  1221. if (highlighted)
  1222. lineClass += ' highlighted';
  1223. code +=
  1224. '<div class="line ' + lineClass + '">'
  1225. + '<table>'
  1226. + '<tr>'
  1227. + (hasGutter ? '<td class="number"><code>' + lineNumber + '</code></td>' : '')
  1228. + '<td class="content">'
  1229. + (spaces != null ? '<code class="spaces">' + spaces.replace(' ', '&nbsp;') + '</code>' : '')
  1230. + line
  1231. + '</td>'
  1232. + '</tr>'
  1233. + '</table>'
  1234. + '</div>'
  1235. ;
  1236. }
  1237. return code;
  1238. },
  1239. /**
  1240. * Finds all matches in the source code.
  1241. * @param {String} code Source code to process matches in.
  1242. * @param {Array} matches Discovered regex matches.
  1243. * @return {String} Returns formatted HTML with processed mathes.
  1244. */
  1245. processMatches: function(code, matches)
  1246. {
  1247. var pos = 0,
  1248. result = '',
  1249. decorate = sh.utils.decorate, // make an alias to save some bytes
  1250. brushName = this.getParam('brush-name', '')
  1251. ;
  1252. function getBrushNameCss(match)
  1253. {
  1254. var result = match ? (match.brushName || brushName) : brushName;
  1255. return result ? result + ' ' : '';
  1256. };
  1257. // Finally, go through the final list of matches and pull the all
  1258. // together adding everything in between that isn't a match.
  1259. for (var i = 0; i < matches.length; i++)
  1260. {
  1261. var match = matches[i],
  1262. matchBrushName
  1263. ;
  1264. if (match === null || match.length === 0)
  1265. continue;
  1266. matchBrushName = getBrushNameCss(match);
  1267. result += decorate(code.substr(pos, match.index - pos), matchBrushName + 'plain')
  1268. + decorate(match.value, matchBrushName + match.css)
  1269. ;
  1270. pos = match.index + match.length;
  1271. }
  1272. // don't forget to add whatever's remaining in the string
  1273. result += decorate(code.substr(pos), getBrushNameCss() + 'plain');
  1274. return result;
  1275. },
  1276. /**
  1277. * Highlights the code and returns complete HTML.
  1278. * @param {String} code Code to highlight.
  1279. * @param {Object} params Parameters object.
  1280. */
  1281. highlight: function(code, params)
  1282. {
  1283. // using variables for shortcuts because JS compressor will shorten local variable names
  1284. var conf = sh.config,
  1285. vars = sh.vars,
  1286. div,
  1287. divClassName,
  1288. tabSize,
  1289. important = 'important'
  1290. ;
  1291. this.params = {};
  1292. this.div = null;
  1293. this.lines = null;
  1294. this.code = null;
  1295. this.bar = null;
  1296. this.toolbarCommands = {};
  1297. this.id = sh.utils.guid('highlighter_');
  1298. // register this instance in the highlighters list
  1299. vars.highlighters[this.id] = this;
  1300. if (code === null)
  1301. code = '';
  1302. // local params take precedence over defaults
  1303. this.params = sh.utils.merge(sh.defaults, params || {});
  1304. // process light mode
  1305. if (this.getParam('light') == true)
  1306. this.params.toolbar = this.params.gutter = false;
  1307. this.div = div = this.create('DIV');
  1308. this.lines = this.create('DIV');
  1309. this.lines.className = 'lines';
  1310. className = 'syntaxhighlighter';
  1311. div.id = this.id;
  1312. // make collapsed
  1313. if (this.getParam('collapse'))
  1314. className += ' collapsed';
  1315. // disable gutter
  1316. if (this.getParam('gutter') == false)
  1317. className += ' nogutter';
  1318. // disable line wrapping
  1319. if (this.getParam('wrap-lines') == false)
  1320. this.lines.className += ' no-wrap';
  1321. // add custom user style name
  1322. className += ' ' + this.getParam('class-name');
  1323. // add brush alias to the class name for custom CSS
  1324. className += ' ' + this.getParam('brush-name');
  1325. div.className = className;
  1326. this.originalCode = code;
  1327. this.code = sh.utils.trimFirstAndLastLines(code)
  1328. .replace(/\r/g, ' ') // IE lets these buggers through
  1329. ;
  1330. tabSize = this.getParam('tab-size');
  1331. // replace tabs with spaces
  1332. this.code = this.getParam('smart-tabs') == true
  1333. ? sh.utils.processSmartTabs(this.code, tabSize)
  1334. : sh.utils.processTabs(this.code, tabSize)
  1335. ;
  1336. this.code = sh.utils.unindent(this.code);
  1337. // add controls toolbar
  1338. if (this.getParam('toolbar'))
  1339. {
  1340. this.bar = this.create('DIV');
  1341. this.bar.className = 'bar';
  1342. this.bar.appendChild(sh.toolbar.create(this));
  1343. div.appendChild(this.bar);
  1344. // set up toolbar rollover
  1345. var bar = this.bar;
  1346. function hide() { bar.className = bar.className.replace('show', ''); }
  1347. div.onmouseover = function() { hide(); bar.className += ' show'; };
  1348. div.onmouseout = function() { hide(); }
  1349. }
  1350. div.appendChild(this.lines);
  1351. this.matches = this.findMatches(this.regexList, this.code);
  1352. this.removeNestedMatches();
  1353. code = this.processMatches(this.code, this.matches);
  1354. // finally, split all lines so that they wrap well
  1355. code = this.createDisplayLines(sh.utils.trim(code));
  1356. // finally, process the links
  1357. if (this.getParam('auto-links'))
  1358. code = sh.utils.processUrls(code);
  1359. this.lines.innerHTML = code;
  1360. },
  1361. /**
  1362. * Converts space separated list of keywords into a regular expression string.
  1363. * @param {String} str Space separated keywords.
  1364. * @return {String} Returns regular expression string.
  1365. */
  1366. getKeywords: function(str)
  1367. {
  1368. str = str
  1369. .replace(/^\s+|\s+$/g, '')
  1370. .replace(/\s+/g, '|')
  1371. ;
  1372. return '\\b(?:' + str + ')\\b';
  1373. },
  1374. /**
  1375. * Makes a brush compatible with the `html-script` functionality.
  1376. * @param {Object} regexGroup Object containing `left` and `right` regular expressions.
  1377. */
  1378. forHtmlScript: function(regexGroup)
  1379. {
  1380. this.htmlScript = {
  1381. left : { regex: regexGroup.left, css: 'script' },
  1382. right : { regex: regexGroup.right, css: 'script' },
  1383. code : new XRegExp(
  1384. "(?<left>" + regexGroup.left.source + ")" +
  1385. "(?<code>.*?)" +
  1386. "(?<right>" + regexGroup.right.source + ")",
  1387. "sgi"
  1388. )
  1389. };
  1390. }
  1391. }; // end of Highlighter
  1392. return sh;
  1393. }(); // end of anonymous function
  1394. /**
  1395. * XRegExp 0.6.1
  1396. * (c) 2007-2008 Steven Levithan
  1397. * <http://stevenlevithan.com/regex/xregexp/>
  1398. * MIT License
  1399. *
  1400. * provides an augmented, cross-browser implementation of regular expressions
  1401. * including support for additional modifiers and syntax. several convenience
  1402. * methods and a recursive-construct parser are also included.
  1403. */
  1404. // prevent running twice, which would break references to native globals
  1405. if (!window.XRegExp) {
  1406. // anonymous function to avoid global variables
  1407. (function () {
  1408. // copy various native globals for reference. can't use the name ``native``
  1409. // because it's a reserved JavaScript keyword.
  1410. var real = {
  1411. exec: RegExp.prototype.exec,
  1412. match: String.prototype.match,
  1413. replace: String.prototype.replace,
  1414. split: String.prototype.split
  1415. },
  1416. /* regex syntax parsing with support for all the necessary cross-
  1417. browser and context issues (escapings, character classes, etc.) */
  1418. lib = {
  1419. part: /(?:[^\\([#\s.]+|\\(?!k<[\w$]+>|[pP]{[^}]+})[\S\s]?|\((?=\?(?!#|<[\w$]+>)))+|(\()(?:\?(?:(#)[^)]*\)|<([$\w]+)>))?|\\(?:k<([\w$]+)>|[pP]{([^}]+)})|(\[\^?)|([\S\s])/g,
  1420. replaceVar: /(?:[^$]+|\$(?![1-9$&`']|{[$\w]+}))+|\$(?:([1-9]\d*|[$&`'])|{([$\w]+)})/g,
  1421. extended: /^(?:\s+|#.*)+/,
  1422. quantifier: /^(?:[?*+]|{\d+(?:,\d*)?})/,
  1423. classLeft: /&&\[\^?/g,
  1424. classRight: /]/g
  1425. },
  1426. indexOf = function (array, item, from) {
  1427. for (var i = from || 0; i < array.length; i++)
  1428. if (array[i] === item) return i;
  1429. return -1;
  1430. },
  1431. brokenExecUndef = /()??/.exec("")[1] !== undefined,
  1432. plugins = {};
  1433. /**
  1434. * Accepts a pattern and flags, returns a new, extended RegExp object.
  1435. * differs from a native regex in that additional flags and syntax are
  1436. * supported and browser inconsistencies are ameliorated.
  1437. * @ignore
  1438. */
  1439. XRegExp = function (pattern, flags) {
  1440. if (pattern instanceof RegExp) {
  1441. if (flags !== undefined)
  1442. throw TypeError("can't supply flags when constructing one RegExp from another");
  1443. return pattern.addFlags(); // new copy
  1444. }
  1445. var flags = flags || "",
  1446. singleline = flags.indexOf("s") > -1,
  1447. extended = flags.indexOf("x") > -1,
  1448. hasNamedCapture = false,
  1449. captureNames = [],
  1450. output = [],
  1451. part = lib.part,
  1452. match, cc, len, index, regex;
  1453. part.lastIndex = 0; // in case the last XRegExp compilation threw an error (unbalanced character class)
  1454. while (match = real.exec.call(part, pattern)) {
  1455. // comment pattern. this check must come before the capturing group check,
  1456. // because both match[1] and match[2] will be non-empty.
  1457. if (match[2]) {
  1458. // keep tokens separated unless the following token is a quantifier
  1459. if (!lib.quantifier.test(pattern.slice(part.lastIndex)))
  1460. output.push("(?:)");
  1461. // capturing group
  1462. } else if (match[1]) {
  1463. captureNames.push(match[3] || null);
  1464. if (match[3])
  1465. hasNamedCapture = true;
  1466. output.push("(");
  1467. // named backreference
  1468. } else if (match[4]) {
  1469. index = indexOf(captureNames, match[4]);
  1470. // keep backreferences separate from subsequent literal numbers
  1471. // preserve backreferences to named groups that are undefined at this point as literal strings
  1472. output.push(index > -1 ?
  1473. "\\" + (index + 1) + (isNaN(pattern.charAt(part.lastIndex)) ? "" : "(?:)") :
  1474. match[0]
  1475. );
  1476. // unicode element (requires plugin)
  1477. } else if (match[5]) {
  1478. output.push(plugins.unicode ?
  1479. plugins.unicode.get(match[5], match[0].charAt(1) === "P") :
  1480. match[0]
  1481. );
  1482. // character class opening delimiter ("[" or "[^")
  1483. // (non-native unicode elements are not supported within character classes)
  1484. } else if (match[6]) {
  1485. if (pattern.charAt(part.lastIndex) === "]") {
  1486. // for cross-browser compatibility with ECMA-262 v3 behavior,
  1487. // convert [] to (?!) and [^] to [\S\s].
  1488. output.push(match[6] === "[" ? "(?!)" : "[\\S\\s]");
  1489. part.lastIndex++;
  1490. } else {
  1491. // parse the character class with support for inner escapes and
  1492. // ES4's infinitely nesting intersection syntax ([&&[^&&[]]]).
  1493. cc = XRegExp.matchRecursive("&&" + pattern.slice(match.index), lib.classLeft, lib.classRight, "", {escapeChar: "\\"})[0];
  1494. output.push(match[6] + cc + "]");
  1495. part.lastIndex += cc.length + 1;
  1496. }
  1497. // dot ("."), pound sign ("#"), or whitespace character
  1498. } else if (match[7]) {
  1499. if (singleline && match[7] === ".") {
  1500. output.push("[\\S\\s]");
  1501. } else if (extended && lib.extended.test(match[7])) {
  1502. len = real.exec.call(lib.extended, pattern.slice(part.lastIndex - 1))[0].length;
  1503. // keep tokens separated unless the following token is a quantifier
  1504. if (!lib.quantifier.test(pattern.slice(part.lastIndex - 1 + len)))
  1505. output.push("(?:)");
  1506. part.lastIndex += len - 1;
  1507. } else {
  1508. output.push(match[7]);
  1509. }
  1510. } else {
  1511. output.push(match[0]);
  1512. }
  1513. }
  1514. regex = RegExp(output.join(""), real.replace.call(flags, /[sx]+/g, ""));
  1515. regex._x = {
  1516. source: pattern,
  1517. captureNames: hasNamedCapture ? captureNames : null
  1518. };
  1519. return regex;
  1520. };
  1521. /**
  1522. * Barebones plugin support for now (intentionally undocumented)
  1523. * @ignore
  1524. * @param {Object} name
  1525. * @param {Object} o
  1526. */
  1527. XRegExp.addPlugin = function (name, o) {
  1528. plugins[name] = o;
  1529. };
  1530. /**
  1531. * Adds named capture support, with values returned as ``result.name``.
  1532. *
  1533. * Also fixes two cross-browser issues, following the ECMA-262 v3 spec:
  1534. * - captured values for non-participating capturing groups should be returned
  1535. * as ``undefined``, rather than the empty string.
  1536. * - the regex's ``lastIndex`` should not be incremented after zero-length
  1537. * matches.
  1538. * @ignore
  1539. */
  1540. RegExp.prototype.exec = function (str) {
  1541. var match = real.exec.call(this, str),
  1542. name, i, r2;
  1543. if (match) {
  1544. // fix browsers whose exec methods don't consistently return
  1545. // undefined for …

Large files files are truncated, but you can click here to view the full file