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 &lt;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					: /(&lt;|<)!--[\s\S]*?--(&gt;|>)/gm,
127			url							: /\w+:\/\/[\w-.\/?%&=:@;]*/g,
128
129			/** <?= ?> tags. */
130			phpScriptTags 				: { left: /(&lt;|<)\?=?/g, right: /\?(&gt;|>)/g },
131
132			/** <%= %> tags. */
133			aspScriptTags				: { left: /(&lt;|<)%=?/g, right: /%(&gt;|>)/g },
134
135			/** &lt;script>&lt;/script> tags. */
136			scriptScriptTags			: { left: /(&lt;|<)\s*script.*?(&gt;|>)/gi, right: /(&lt;|<)\/\s*script\s*(&gt;|>)/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 &lt;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 &lt;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, '&lt;');
780
781		// Replace two or more sequential spaces with &nbsp; 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(/^(&nbsp;| )+/, 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*\/?>|&lt;br\s*\/?&gt;/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 = /(.*)((&gt;|&lt;).*)/;
1038
1039		return code.replace(sh.regexLib.url, function(m)
1040		{
1041			var suffix = '',
1042				match = null
1043				;
1044
1045			// We include &lt; and &gt; in the URL for the common cases like <http://google.com>
1046			// The problem is that they get transformed into &lt;http://google.com&gt;
1047			// Where as &gt; 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 &lt;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[]]&gt; from &lt;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[]]&gt; tags.
1083	 */
1084	function stripCData(original)
1085	{
1086		var left = '<![CDATA[',
1087			right = ']]&gt;',
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, '&lt;'); // 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 = /^(&nbsp;|\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