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