PageSourceSearch

https://braviaderm.com/DesktopModules/Blog/Templates/classicBlog/js/shCore.js?cdv=870

js braviaderm.com collected 2026-09-26 10:21:43 UTC 76,854 bytes, 2,501 lines download raw bytes

1// XRegExp 1.5.1
2// (c) 2007-2012 Steven Levithan
3// MIT License
4// <http://xregexp.com>
5// Provides an augmented, extensible, cross-browser implementation of regular expressions,
6// including support for additional syntax, flags, and methods
7
8var XRegExp;
9
10if (XRegExp) {
11    // Avoid running twice, since that would break references to native globals
12    throw Error("can't load XRegExp twice in the same frame");
13}
14
15// Run within an anonymous function to protect variables and avoid new globals
16(function (undefined) {
17
18    //---------------------------------
19    //  Constructor
20    //---------------------------------
21
22    // Accepts a pattern and flags; returns a new, extended `RegExp` object. Differs from a native
23    // regular expression in that additional syntax and flags are supported and cross-browser
24    // syntax inconsistencies are ameliorated. `XRegExp(/regex/)` clones an existing regex and
25    // converts to type XRegExp
26    XRegExp = function (pattern, flags) {
27        var output = [],
28            currScope = XRegExp.OUTSIDE_CLASS,
29            pos = 0,
30            context, tokenResult, match, chr, regex;
31
32        if (XRegExp.isRegExp(pattern)) {
33            if (flags !== undefined)
34                throw TypeError("can't supply flags when constructing one RegExp from another");
35            return clone(pattern);
36        }
37        // Tokens become part of the regex construction process, so protect against infinite
38        // recursion when an XRegExp is constructed within a token handler or trigger
39        if (isInsideConstructor)
40            throw Error("can't call the XRegExp constructor within token definition functions");
41
42        flags = flags || "";
43        context = { // `this` object for custom tokens
44            hasNamedCapture: false,
45            captureNames: [],
46            hasFlag: function (flag) {return flags.indexOf(flag) > -1;},
47            setFlag: function (flag) {flags += flag;}
48        };
49
50        while (pos < pattern.length) {
51            // Check for custom tokens at the current position
52            tokenResult = runTokens(pattern, pos, currScope, context);
53
54            if (tokenResult) {
55                output.push(tokenResult.output);
56                pos += (tokenResult.match[0].length || 1);
57            } else {
58                // Check for native multicharacter metasequences (excluding character classes) at
59                // the current position
60                if (match = nativ.exec.call(nativeTokens[currScope], pattern.slice(pos))) {
61                    output.push(match[0]);
62                    pos += match[0].length;
63                } else {
64                    chr = pattern.charAt(pos);
65                    if (chr === "[")
66                        currScope = XRegExp.INSIDE_CLASS;
67                    else if (chr === "]")
68                        currScope = XRegExp.OUTSIDE_CLASS;
69                    // Advance position one character
70                    output.push(chr);
71                    pos++;
72                }
73            }
74        }
75
76        regex = RegExp(output.join(""), nativ.replace.call(flags, flagClip, ""));
77        regex._xregexp = {
78            source: pattern,
79            captureNames: context.hasNamedCapture ? context.captureNames : null
80        };
81        return regex;
82    };
83
84
85    //---------------------------------
86    //  Public properties
87    //---------------------------------
88
89    XRegExp.version = "1.5.1";
90
91    // Token scope bitflags
92    XRegExp.INSIDE_CLASS = 1;
93    XRegExp.OUTSIDE_CLASS = 2;
94
95
96    //---------------------------------
97    //  Private variables
98    //---------------------------------
99
100    var replacementToken = /\$(?:(\d\d?|[$&`'])|{([$\w]+)})/g,
101        flagClip = /[^gimy]+|([\s\S])(?=[\s\S]*\1)/g, // Nonnative and duplicate flags
102        quantifier = /^(?:[?*+]|{\d+(?:,\d*)?})\??/,
103        isInsideConstructor = false,
104        tokens = [],
105        // Copy native globals for reference ("native" is an ES3 reserved keyword)
106        nativ = {
107            exec: RegExp.prototype.exec,
108            test: RegExp.prototype.test,
109            match: String.prototype.match,
110            replace: String.prototype.replace,
111            split: String.prototype.split
112        },
113        compliantExecNpcg = nativ.exec.call(/()??/, "")[1] === undefined, // check `exec` handling of nonp
113articipating capturing groups
114        compliantLastIndexIncrement = function () {
115            var x = /^/g;
116            nativ.test.call(x, "");
117            return !x.lastIndex;
118        }(),
119        hasNativeY = RegExp.prototype.sticky !== undefined,
120        nativeTokens = {};
121
122    // `nativeTokens` match native multicharacter metasequences only (including deprecated octals,
123    // excluding character classes)
124    nativeTokens[XRegExp.INSIDE_CLASS] = /^(?:\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S]))/;
125    nativeTokens[XRegExp.OUTSIDE_CLASS] = /^(?:\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\d*|x[\dA-Fa-f]{2}|u[\dA-Fa-f]{4}|c[A-Za-z]|[\s\S])|\(\?[:=!]|[?*+]\?|{\d+(?:,\d*)?}\??)/;
126
127
128    //---------------------------------
129    //  Public methods
130    //---------------------------------
131
132    // Lets you extend or change XRegExp syntax and create custom flags. This is used internally by
133    // the XRegExp library and can be used to create XRegExp plugins. This function is intended for
134    // users with advanced knowledge of JavaScript's regular expression syntax and behavior. It can
135    // be disabled by `XRegExp.freezeTokens`
136    XRegExp.addToken = function (regex, handler, scope, trigger) {
137        tokens.push({
138            pattern: clone(regex, "g" + (hasNativeY ? "y" : "")),
139            handler: handler,
140            scope: scope || XRegExp.OUTSIDE_CLASS,
141            trigger: trigger || null
142        });
143    };
144
145    // Accepts a pattern and flags; returns an extended `RegExp` object. If the pattern and flag
146    // combination has previously been cached, the cached copy is returned; otherwise the newly
147    // created regex is cached
148    XRegExp.cache = function (pattern, flags) {
149        var key = pattern + "/" + (flags || "");
150        return XRegExp.cache[key] || (XRegExp.cache[key] = XRegExp(pattern, flags));
151    };
152
153    // Accepts a `RegExp` instance; returns a copy with the `/g` flag set. The copy has a fresh
154    // `lastIndex` (set to zero). If you want to copy a regex without forcing the `global`
155    // property, use `XRegExp(regex)`. Do not use `RegExp(regex)` because it will not preserve
156    // special properties required for named capture
157    XRegExp.copyAsGlobal = function (regex) {
158        return clone(regex, "g");
159    };
160
161    // Accepts a string; returns the string with regex metacharacters escaped. The returned string
162    // can safely be used at any point within a regex to match the provided literal string. Escaped
163    // characters are [ ] { } ( ) * + ? - . , \ ^ $ | # and whitespace
164    XRegExp.escape = function (str) {
165        return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
166    };
167
168    // Accepts a string to search, regex to search with, position to start the search within the
169    // string (default: 0), and an optional Boolean indicating whether matches must start at-or-
170    // after the position or at the specified position only. This function ignores the `lastIndex`
171    // of the provided regex in its own handling, but updates the property for compatibility
172    XRegExp.execAt = function (str, regex, pos, anchored) {
173        var r2 = clone(regex, "g" + ((anchored && hasNativeY) ? "y" : "")),
174            match;
175        r2.lastIndex = pos = pos || 0;
176        match = r2.exec(str); // Run the altered `exec` (required for `lastIndex` fix, etc.)
177        if (anchored && match && match.index !== pos)
178            match = null;
179        if (regex.global)
180            regex.lastIndex = match ? r2.lastIndex : 0;
181        return match;
182    };
183
184    // Breaks the unrestorable link to XRegExp's private list of tokens, thereby preventing
185    // syntax and flag changes. Should be run after XRegExp and any plugins are loaded
186    XRegExp.freezeTokens = function () {
187        XRegExp.addToken = function () {
188            throw Error("can't run addToken after freezeTokens");
189        };
190    };
191
192    // Accepts any value; returns a Boolean indicating whether the argument is a `RegExp` object.
193    // Note that this is also `true` for regex literals and regexes created by the `XReg
193Exp`
194    // constructor. This works correctly for variables created in another frame, when `instanceof`
195    // and `constructor` checks would fail to work as intended
196    XRegExp.isRegExp = function (o) {
197        return Object.prototype.toString.call(o) === "[object RegExp]";
198    };
199
200    // Executes `callback` once per match within `str`. Provides a simpler and cleaner way to
201    // iterate over regex matches compared to the traditional approaches of subverting
202    // `String.prototype.replace` or repeatedly calling `exec` within a `while` loop
203    XRegExp.iterate = function (str, regex, callback, context) {
204        var r2 = clone(regex, "g"),
205            i = -1, match;
206        while (match = r2.exec(str)) { // Run the altered `exec` (required for `lastIndex` fix, etc.)
207            if (regex.global)
208                regex.lastIndex = r2.lastIndex; // Doing this to follow expectations if `lastIndex` is checked within `callback`
209            callback.call(context, match, ++i, str, regex);
210            if (r2.lastIndex === match.index)
211                r2.lastIndex++;
212        }
213        if (regex.global)
214            regex.lastIndex = 0;
215    };
216
217    // Accepts a string and an array of regexes; returns the result of using each successive regex
218    // to search within the matches of the previous regex. The array of regexes can also contain
219    // objects with `regex` and `backref` properties, in which case the named or numbered back-
220    // references specified are passed forward to the next regex or returned. E.g.:
221    // var xregexpImgFileNames = XRegExp.matchChain(html, [
222    //     {regex: /<img\b([^>]+)>/i, backref: 1}, // <img> tag attributes
223    //     {regex: XRegExp('(?ix) \\s src=" (?<src> [^"]+ )'), backref: "src"}, // src attribute values
224    //     {regex: XRegExp("^http://xregexp\\.com(/[^#?]+)", "i"), backref: 1}, // xregexp.com paths
225    //     /[^\/]+$/ // filenames (strip directory paths)
226    // ]);
227    XRegExp.matchChain = function (str, chain) {
228        return function recurseChain (values, level) {
229            var item = chain[level].regex ? chain[level] : {regex: chain[level]},
230                regex = clone(item.regex, "g"),
231                matches = [], i;
232            for (i = 0; i < values.length; i++) {
233                XRegExp.iterate(values[i], regex, function (match) {
234                    matches.push(item.backref ? (match[item.backref] || "") : match[0]);
235                });
236            }
237            return ((level === chain.length - 1) || !matches.length) ?
238                matches : recurseChain(matches, level + 1);
239        }([str], 0);
240    };
241
242
243    //---------------------------------
244    //  New RegExp prototype methods
245    //---------------------------------
246
247    // Accepts a context object and arguments array; returns the result of calling `exec` with the
248    // first value in the arguments array. the context is ignored but is accepted for congruity
249    // with `Function.prototype.apply`
250    RegExp.prototype.apply = function (context, args) {
251        return this.exec(args[0]);
252    };
253
254    // Accepts a context object and string; returns the result of calling `exec` with the provided
255    // string. the context is ignored but is accepted for congruity with `Function.prototype.call`
256    RegExp.prototype.call = function (context, str) {
257        return this.exec(str);
258    };
259
260
261    //---------------------------------
262    //  Overriden native methods
263    //---------------------------------
264
265    // Adds named capture support (with backreferences returned as `result.name`), and fixes two
266    // cross-browser issues per ES3:
267    // - Captured values for nonparticipating capturing groups should be returned as `undefined`,
268    //   rather than the empty string.
269    // - `lastIndex` should not be incremented after zero-length matches.
270    RegExp.prototype.exec = function (str) {
271        var match, name, r2, origLastIndex;
272        if (!this.global)
273            origLastIndex = this.lastIndex;
274        match = nativ.exec.apply(this, arguments);
275        if (match) {
276            // Fix browsers whose `exec` methods don't consistently return `undefined` for
277            // nonparticipating capturing groups
278            if (!compliantExecNpcg && match.length > 1 && indexOf(match, "") > -1) {
279                r2 = RegExp(this.source, nativ.replace.call(getNativeFlags(this), "g", ""));
280                // Using `str.slice(match.index)` rather than `match[0]` in case lookahead allowed
281                // matching due to characters outside the match
282                nativ.replace.call((str + "").slice(match.index), r2, function () {
283                    for (var i = 1; i < arguments.length - 2; i++) {
284                        if (arguments[i] === undefined)
285                            match[i] = undefined;
286                    }
287                });
288            }
289            // Attach named capture properties
290            if (this._xregexp && this._xregexp.captureNames) {
291                for (var i = 1; i < match.length; i++) {
292                    name = this._xregexp.captureNames[i - 1];
293                    if (name)
294                       match[name] = match[i];
295                }
296            }
297            // Fix browsers that increment `lastIndex` after zero-length matches
298            if (!compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index))
299                this.lastIndex--;
300        }
301        if (!this.global)
302            this.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows)
303        return match;
304    };
305
306    // Fix browser bugs in native method
307    RegExp.prototype.test = function (str) {
308        // Use the native `exec` to skip some processing overhead, even though the altered
309        // `exec` would take care of the `lastIndex` fixes
310        var match, origLastIndex;
311        if (!this.global)
312            origLastIndex = this.lastIndex;
313        match = nativ.exec.call(this, str);
314        // Fix browsers that increment `lastIndex` after zero-length matches
315        if (match && !compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index))
316            this.lastIndex--;
317        if (!this.global)
318            this.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows)
319        return !!match;
320    };
321
322    // Adds named capture support and fixes browser bugs in native method
323    String.prototype.match = function (regex) {
324        if (!XRegExp.isRegExp(regex))
325            regex = RegExp(regex); // Native `RegExp`
326        if (regex.global) {
327            var result = nativ.match.apply(this, arguments);
328            regex.lastIndex = 0; // Fix IE bug
329            return result;
330        }
331        return regex.exec(this); // Run the altered `exec`
332    };
333
334    // Adds support for `${n}` tokens for named and numbered backreferences in replacement text,
335    // and provides named backreferences to replacement functions as `arguments[0].name`. Also
336    // fixes cross-browser differences in replacement text syntax when performing a replacement
337    // using a nonregex search value, and the value of replacement regexes' `lastIndex` property
338    // during replacement iterations. Note that this doesn't support SpiderMonkey's proprietary
339    // third (`flags`) parameter
340    String.prototype.replace = function (search, replacement) {
341        var isRegex = XRegExp.isRegExp(search),
342            captureNames, result, str, origLastIndex;
343
344        // There are too many combinations of search/replacement types/values and browser bugs that
345        // preclude passing to native `replace`, so don't try
346        //if (...)
347        //    return nativ.replace.apply(this, arguments);
348
349        if (isRegex) {
350            if (search._xregexp)
351                captureNames = search._xregexp.captureNames; // Array or `null`
352            if (!search.global)
353                origLastIndex = search.lastIndex;
354        } else {
355            search = search + ""; // Type conversion
356        }
357
358        if (Object.prototype.toString.call(replacement) === "[object Function]") {
359            result = nativ.replace.call(this + "", search, function () {
360                if (captureNames) {
361                    // Change the `arguments[0]` string primitive to a String object which can store properties
362                    arguments[0] = new String(arguments[0]);
363                    // Store named backreferences on `arguments[0]`
364                    for (var i = 0; i < captureNames.length; i++) {
365                        if (captureNames[i])
366                            arguments[0][captureNames[i]] = arguments[i + 1];
367                    }
368                }
369                // Update `lastIndex` before calling `replacement` (fix browsers)
370                if (isRegex && search.global)
371                    search.lastIndex = arguments[arguments.length - 2] + arguments[0].length;
372                return replacement.apply(null, arguments);
373            });
374        } else {
375            str = this + ""; // Type conversion, so `args[args.length - 1]` will be a string (given nonstring `this`)
376            result = nativ.replace.call(str, search, function () {
377                var args = arguments; // Keep this function's `arguments` available through closure
378                return nativ.replace.call(replacement + "", replacementToken, function ($0, $1, $2) {
379                    // Numbered backreference (without delimiters) or special variable
380                    if ($1) {
381                        switch ($1) {
382                            case "$": return "$";
383                            case "&": return args[0];
384                            case "`": return args[args.length - 1].slice(0, args[args.length - 2]);
385                            case "'": return args[args.length - 1].slice(args[args.length - 2] + args[0].length);
386                            // Numbered backreference
387                            default:
388                                // What does "$10" mean?
389                                // - Backreference 10, if 10 or more capturing groups exist
390                                // - Backreference 1 followed by "0", if 1-9 capturing groups exist
391                                // - Otherwise, it's the string "$10"
392                                // Also note:
393                                // - Backreferences cannot be more than two digits (enforced by `replacementToken`)
394                                // - "$01" is equivalent to "$1" if a capturing group exists, otherwise it's the string "$01"
395                                // - There is no "$0" token ("$&" is the entire match)
396                                var literalNumbers = "";
397                                $1 = +$1; // Type conversion; drop leading zero
398                                if (!$1) // `$1` was "0" or "00"
399                                    return $0;
400                                while ($1 > args.length - 3) {
401                                    literalNumbers = String.prototype.slice.call($1, -1) + literalNumbers;
402                                    $1 = Math.floor($1 / 10); // Drop the last digit
403                                }
404                                return ($1 ? args[$1] || "" : "$") + literalNumbers;
405                        }
406                    // Named backreference or delimited numbered backreference
407                    } else {
408                        // What does "${n}" mean?
409                        // - Backreference to numbered capture n. Two differences from "$n":
410                        //   - n can be more than two digits
411                        //   - Backreference 0 is allowed, and is the entire match
412                        // - Backreference to named capture n, if it exists and is not a number overridden by numbered capture
413                        // - Otherwise, it's the string "${n}"
414                        var n = +$2; // Type conversion; drop leading zeros
415                        if (n <= args.length - 3)
416                            return args[n];
417                        n = captureNames ? indexOf(captureNames, $2) : -1;
418                        return n > -1 ? args[n + 1] : $0;
419                    }
420                });
421            });
422        }
423
424        if (isRegex) {
425            if (search.global)
426                search.lastIndex = 0; // Fix IE, Safari bug (last tested IE 9.0.5, Safari 5.1.2 on Windows)
427            else
428                search.lastIndex = origLastIndex; // Fix IE, Opera bug (last tested IE 9.0.5, Opera 11.61 on Windows)
429        }
430
431        return result;
432    };
433
434    // A consistent cross-browser, ES3 compliant `split`
435    String.prototype.split = function (s /* separator */, limit) {
436        // If separator `s` is not a regex, use the native `split`
437        if (!XRegExp.isRegExp(s))
438            return nativ.split.apply(this, arguments);
439
440        var str = this + "", // Type conversion
441            output = [],
442            lastLastIndex = 0,
443            match, lastLength;
444
445        // Behavior for `limit`: if it's...
446        // - `undefined`: No limit
447        // - `NaN` or zero: Return an empty array
448        // - A positive number: Use `Math.floor(limit)`
449        // - A negative number: No limit
450        // - Other: Type-convert, then use the above rules
451        if (limit === undefined || +limit < 0) {
452            limit = Infinity;
453        } else {
454            limit = Math.floor(+limit);
455            if (!limit)
456                return [];
457        }
458
459        // This is required if not `s.global`, and it avoids needing to set `s.lastIndex` to zero
460        // and restore it to its original value when we're done using the regex
461        s = XRegExp.copyAsGlobal(s);
462
463        while (match = s.exec(str)) { // Run the altered `exec` (required for `lastIndex` fix, etc.)
464            if (s.lastIndex > lastLastIndex) {
465                output.push(str.slice(lastLastIndex, match.index));
466
467                if (match.length > 1 && match.index < str.length)
468                    Array.prototype.push.apply(output, match.slice(1));
469
470                lastLength = match[0].length;
471                lastLastIndex = s.lastIndex;
472
473                if (output.length >= limit)
474                    break;
475            }
476
477            if (s.lastIndex === match.index)
478                s.lastIndex++;
479        }
480
481        if (lastLastIndex === str.length) {
482            if (!nativ.test.call(s, "") || lastLength)
483                output.push("");
484        } else {
485            output.push(str.slice(lastLastIndex));
486        }
487
488        return output.length > limit ? output.slice(0, limit) : output;
489    };
490
491
492    //---------------------------------
493    //  Private helper functions
494    //---------------------------------
495
496    // Supporting function for `XRegExp`, `XRegExp.copyAsGlobal`, etc. Returns a copy of a `RegExp`
497    // instance with a fresh `lastIndex` (set to zero), preserving properties required for named
498    // capture. Also allows adding new flags in the process of copying the regex
499    function clone (regex, additionalFlags) {
500        if (!XRegExp.isRegExp(regex))
501            throw TypeError("type RegExp expected");
502        var x = regex._xregexp;
503        regex = XRegExp(regex.source, getNativeFlags(regex) + (additionalFlags || ""));
504        if (x) {
505            regex._xregexp = {
506                source: x.source,
507                captureNames: x.captureNames ? x.captureNames.slice(0) : null
508            };
509        }
510        return regex;
511    }
512
513    function getNativeFlags (regex) {
514        return (regex.global     ? "g" : "") +
515               (regex.ignoreCase ? "i" : "") +
516               (regex.multiline  ? "m" : "") +
517               (regex.extended   ? "x" : "") + // Proposed for ES4; included in AS3
518               (regex.sticky     ? "y" : "");
519    }
520
521    function runTokens (pattern, index, scope, context) {
522        var i = tokens.length,
523            result, match, t;
524        // Protect against constructing XRegExps within token handler and trigger functions
525        isInsideConstructor = true;
526        // Must reset `isInsideConstructor`, even if a `trigger` or `handler` throws
527        try {
528            while (i--) { // Run in reverse order
529                t = tokens[i];
530                if ((scope & t.scope) && (!t.trigger || t.trigger.call(context))) {
531                    t.pattern.lastIndex = index;
532                    match = t.pattern.exec(pattern); // Running the altered `exec` here allows use of named backreferences, etc.
533                    if (match && match.index === index) {
534                        result = {
535                            output: t.handler.call(context, match, scope),
536                            match: match
537                        };
538                        break;
539                    }
540                }
541            }
542        } catch (err) {
543            throw err;
544        } finally {
545            isInsideConstructor = false;
546        }
547        return result;
548    }
549
550    function indexOf (array, item, from) {
551        if (Array.prototype.indexOf) // Use the native array method if available
552            return array.indexOf(item, from);
553        for (var i = from || 0; i < array.length; i++) {
554            if (array[i] === item)
555                return i;
556        }
557        return -1;
558    }
559
560
561    //---------------------------------
562    //  Built-in tokens
563    //---------------------------------
564
565    // Augment XRegExp's regular expression syntax and flags. Note that when adding tokens, the
566    // third (`scope`) argument defaults to `XRegExp.OUTSIDE_CLASS`
567
568    // Comment pattern: (?# )
569    XRegExp.addToken(
570        /\(\?#[^)]*\)/,
571        function (match) {
572            // Keep tokens separated unless the following token is a quantifier
573            return nativ.test.call(quantifier, match.input.slice(match.index + match[0].length)) ? "" : "(?:)";
574        }
575    );
576
577    // Capturing group (match the opening parenthesis only).
578    // Required for support of named capturing groups
579    XRegExp.addToken(
580        /\((?!\?)/,
581        function () {
582            this.captureNames.push(null);
583            return "(";
584        }
585    );
586
587    // Named capturing group (match the opening delimiter only): (?<name>
588    XRegExp.addToken(
589        /\(\?<([$\w]+)>/,
590        function (match) {
591            this.captureNames.push(match[1]);
592            this.hasNamedCapture = true;
593            return "(";
594        }
595    );
596
597    // Named backreference: \k<name>
598    XRegExp.addToken(
599        /\\k<([\w$]+)>/,
600        function (match) {
601            var index = indexOf(this.captureNames, match[1]);
602            // Keep backreferences separate from subsequent literal numbers. Preserve back-
603            // references to named groups that are undefined at this point as literal strings
604            return index > -1 ?
605                "\\" + (index + 1) + (isNaN(match.input.charAt(match.index + match[0].length)) ? "" : "(?:)") :
606                match[0];
607        }
608    );
609
610    // Empty character class: [] or [^]
611    XRegExp.addToken(
612        /\[\^?]/,
613        function (match) {
614            // For cross-browser compatibility with ES3, convert [] to \b\B and [^] to [\s\S].
615            // (?!) should work like \b\B, but is unreliable in Firefox
616            return match[0] === "[]" ? "\\b\\B" : "[\\s\\S]";
617        }
618    );
619
620    // Mode modifier at the start of the pattern only, with any combination of flags imsx: (?imsx)
621    // Does not support x(?i), (?-i), (?i-m), (?i: ), (?i)(?m), etc.
622    XRegExp.addToken(
623        /^\(\?([imsx]+)\)/,
624        function (match) {
625            this.setFlag(match[1]);
626            return "";
627        }
628    );
629
630    // Whitespace and comments, in free-spacing (aka extended) mode only
631    XRegExp.addToken(
632        /(?:\s+|#.*)+/,
633        function (match) {
634            // Keep tokens separated unless the following token is a quantifier
635            return nativ.test.call(quantifier, match.input.slice(match.index + match[0].length)) ? "" : "(?:)";
636        },
637        XRegExp.OUTSIDE_CLASS,
638        function () {return this.hasFlag("x");}
639    );
640
641    // Dot, in dotall (aka singleline) mode only
642    XRegExp.addToken(
643        /\./,
644        function () {return "[\\s\\S]";},
645        XRegExp.OUTSIDE_CLASS,
646        function () {return this.hasFlag("s");}
647    );
648
649
650    //---------------------------------
651    //  Backward compatibility
652    //---------------------------------
653
654    // Uncomment the following block for compatibility with XRegExp 1.0-1.2:
655    /*
656    XRegExp.matchWithinChain = XRegExp.matchChain;
657    RegExp.prototype.addFlags = function (s) {return clone(this, s);};
658    RegExp.prototype.execAll = function (s) {var r = []; XRegExp.iterate(s, this, function (m) {r.push(m);}); return r;};
659    RegExp.prototype.forEachExec = function (s, f, c) {return XRegExp.iterate(s, this, f, c);};
660    RegExp.prototype.validate = function (s) {var r = RegExp("^(?:" + this.source + ")$(?!\\s)", getNativeFlags(this)); if (this.global) this.lastIndex = 0; return s.search(r) === 0;};
661    */
662
663})();
664
665//
666// Begin anonymous function. This is used to contain local scope variables without polutting global scope.
667//
668if (typeof(SyntaxHighlighter) == 'undefined') var SyntaxHighlighter = function() { 
669
670// CommonJS
671if (typeof(require) != 'undefined' && typeof(XRegExp) == 'undefined')
672{
673	XRegExp = require('XRegExp').XRegExp;
674}
675
676// Shortcut object which will be assigned to the SyntaxHighlighter variable.
677// This is a shorthand for local reference in order to avoid long namespace 
678// references to SyntaxHighlighter.whatever...
679var sh = {
680	defaults : {
681		/** Additional CSS class names to be added to highlighter elements. */
682		'class-name' : '',
683		
684		/** First line number. */
685		'first-line' : 1,
686		
687		/**
688		 * Pads line numbers. Possible values are:
689		 *
690		 *   false - don't pad line numbers.
691		 *   true  - automaticaly pad numbers with minimum required number of leading zeroes.
692		 *   [int] - length up to which pad line numbers.
693		 */
694		'pad-line-numbers' : false,
695		
696		/** Lines to highlight. */
697		'highlight' : null,
698		
699		/** Title to be displayed above the code block. */
700		'title' : null,
701		
702		/** Enables or disables smart tabs. */
703		'smart-tabs' : true,
704		
705		/** Gets or sets tab size. */
706		'tab-size' : 4,
707		
708		/** Enables or disables gutter. */
709		'gutter' : true,
710		
711		/** Enables or disables toolbar. */
712		'toolbar' : true,
713		
714		/** Enables quick code copy and paste from double click. */
715		'quick-code' : true,
716		
717		/** Forces code view to be collapsed. */
718		'collapse' : false,
719		
720		/** Enables or disables automatic links. */
721		'auto-links' : true,
722		
723		/** Gets or sets light mode. Equavalent to turning off gutter and toolbar. */
724		'light' : false,
725
726		'unindent' : true,
727		
728		'html-script' : false
729	},
730	
731	config : {
732		space : '&nbsp;',
733		
734		/** Enables use of <SCRIPT type="syntaxhighlighter" /> tags. */
735		useScriptTags : true,
736		
737		/** Blogger mode flag. */
738		bloggerMode : false,
739		
740		stripBrs : false,
741		
742		/** Name of the tag that SyntaxHighlighter will automatically look for. */
743		tagName : 'pre',
744		
745		strings : {
746			expandSource : 'expand source',
747			help : '?',
748			alert: 'SyntaxHighlighter\n\n',
749			noBrush : 'Can\'t find brush for: ',
750			brushNotHtmlScript : 'Brush wasn\'t configured for html-script option: ',
751			
752			// this is populated by the build script
753			aboutDialog : '@ABOUT@'
754		}
755	},
756	
757	/** Internal 'global' variables. */
758	vars : {
759		discoveredBrushes : null,
760		highlighters : {}
761	},
762	
763	/** This object is populated by user included external brush files. */
764	brushes : {},
765
766	/** Common regular expressions. */
767	regexLib : {
768		multiLineCComments			: /\/\*[\s\S]*?\*\//gm,
769		singleLineCComments			: /\/\/.*$/gm,
770		singleLinePerlComments		: /#.*$/gm,
771		doubleQuotedString			: /"([^\\"\n]|\\.)*"/g,
772		singleQuotedString			: /'([^\\'\n]|\\.)*'/g,
773		multiLineDoubleQuotedString	: new XRegExp('"([^\\\\"]|\\\\.)*"', 'gs'),
774		multiLineSingleQuotedString	: new XRegExp("'([^\\\\']|\\\\.)*'", 'gs'),
775		xmlComments					: /(&lt;|<)!--[\s\S]*?--(&gt;|>)/gm,
776		url							: /\w+:\/\/[\w-.\/?%&=:@;#]*/g,
777		
778		/** <?= ?> tags. */
779		phpScriptTags 				: { left: /(&lt;|<)\?(?:=|php)?/g, right: /\?(&gt;|>)/g, 'eof' : true },
780		
781		/** <%= %> tags. */
782		aspScriptTags				: { left: /(&lt;
782|<)%=?/g, right: /%(&gt;|>)/g },
783		
784		/** <script> tags. */
785		scriptScriptTags			: { left: /(&lt;|<)\s*script.*?(&gt;|>)/gi, right: /(&lt;|<)\/\s*script\s*(&gt;|>)/gi }
786	},
787
788	toolbar: {
789		/**
790		 * Generates HTML markup for the toolbar.
791		 * @param {Highlighter} highlighter Highlighter instance.
792		 * @return {String} Returns HTML markup.
793		 */
794		getHtml: function(highlighter)
795		{
796			var html = '<div class="toolbar">',
797				items = sh.toolbar.items,
798				list = items.list
799				;
800			
801			function defaultGetHtml(highlighter, name)
802			{
803				return sh.toolbar.getButtonHtml(highlighter, name, sh.config.strings[name]);
804			};
805			
806			for (var i = 0; i < list.length; i++)
807				html += (items[list[i]].getHtml || defaultGetHtml)(highlighter, list[i]);
808			
809			html += '</div>';
810			
811			return html;
812		},
813		
814		/**
815		 * Generates HTML markup for a regular button in the toolbar.
816		 * @param {Highlighter} highlighter Highlighter instance.
817		 * @param {String} commandName		Command name that would be executed.
818		 * @param {String} label			Label text to display.
819		 * @return {String}					Returns HTML markup.
820		 */
821		getButtonHtml: function(highlighter, commandName, label)
822		{
823			return '<span><a href="#" class="toolbar_item'
824				+ ' command_' + commandName
825				+ ' ' + commandName
826				+ '">' + label + '</a></span>'
827				;
828		},
829		
830		/**
831		 * Event handler for a toolbar anchor.
832		 */
833		handler: function(e)
834		{
835			var target = e.target,
836				className = target.className || ''
837				;
838
839			function getValue(name)
840			{
841				var r = new RegExp(name + '_(\\w+)'),
842					match = r.exec(className)
843					;
844
845				return match ? match[1] : null;
846			};
847			
848			var highlighter = getHighlighterById(findParentElement(target, '.syntaxhighlighter').id),
849				commandName = getValue('command')
850				;
851			
852			// execute the toolbar command
853			if (highlighter && commandName)
854				sh.toolbar.items[commandName].execute(highlighter);
855
856			// disable default A click behaviour
857			e.preventDefault();
858		},
859		
860		/** Collection of toolbar items. */
861		items : {
862			// Ordered lis of items in the toolbar. Can't expect `for (var n in items)` to be consistent.
863			list: ['expandSource', 'help'],
864
865			expandSource: {
866				getHtml: function(highlighter)
867				{
868					if (highlighter.getParam('collapse') != true)
869						return '';
870						
871					var title = highlighter.getParam('title');
872					return sh.toolbar.getButtonHtml(highlighter, 'expandSource', title ? title : sh.config.strings.expandSource);
873				},
874			
875				execute: function(highlighter)
876				{
877					var div = getHighlighterDivById(highlighter.id);
878					removeClass(div, 'collapsed');
879				}
880			},
881
882			/** Command to display the about dialog window. */
883			help: {
884				execute: function(highlighter)
885				{	
886					var wnd = popup('', '_blank', 500, 250, 'scrollbars=0'),
887						doc = wnd.document
888						;
889					
890					doc.write(sh.config.strings.aboutDialog);
891					doc.close();
892					wnd.focus();
893				}
894			}
895		}
896	},
897
898	/**
899	 * Finds all elements on the page which should be processes by SyntaxHighlighter.
900	 *
901	 * @param {Object} globalParams		Optional parameters which override element's 
902	 * 									parameters. Only used if element is specified.
903	 * 
904	 * @param {Object} element	Optional element to highlight. If none is
905	 * 							provided, all elements in the current document 
906	 * 							are returned which qualify.
907	 *
908	 * @return {Array}	Returns list of <code>{ target: DOMElement, params: Object }</code> objects.
909	 */
910	findElements: function(globalParams, element)
911	{
912		var elements = element ? [element] : toArray(document.getElementsByTagName(sh.config.tagName)), 
913			conf = sh.config,
914			result = []
915			;
916
917		// support for <SCRIPT TYPE="syntaxhighlighter" /> feature
918		if (conf.useScriptTags)
919			elements = elements.concat(getSyntaxHighlighterScriptTags());
920
921		if (elements.length === 0) 
922			return result;
923	
924		for (var i = 0; i < elements.length; i++) 
925		{
926			var item = {
927				target: elements[i], 
928				// local params take precedence over globals
929				params: merge(globalParams, parseParams(elements[i].className))
930			};
931
932			if (item.params['brush'] == null)
933				continue;
934				
935			result.push(item);
936		}
937		
938		return result;
939	},
940
941	/**
942	 * Shorthand to highlight all elements on the page that are marked as 
943	 * SyntaxHighlighter source code.
944	 * 
945	 * @param {Object} globalParams		Optional parameters which override element's 
946	 * 									parameters. Only used if element is specified.
947	 * 
948	 * @param {Object} element	Optional element to highlight. If none is
949	 * 							provided, all elements in the current document 
950	 * 							are highlighted.
951	 */ 
952	highlight: function(globalParams, element)
953	{
954		var elements = this.findElements(globalParams, element),
955			propertyName = 'innerHTML', 
956			highlighter = null,
957			conf = sh.config
958			;
959
960		if (elements.length === 0) 
961			return;
962	
963		for (var i = 0; i < elements.length; i++) 
964		{
965			var element = elements[i],
966				target = element.target,
967				params = element.params,
968				brushName = params.brush,
969				code
970				;
971
972			if (brushName == null)
973				continue;
974
975			// Instantiate a brush
976			if (params['html-script'] == 'true' || sh.defaults['html-script'] == true) 
977			{
978				highlighter = new sh.HtmlScript(brushName);
979				brushName = 'htmlscript';
980			}
981			else
982			{
983				var brush = findBrush(brushName);
984				
985				if (brush)
986					highlighter = new brush();
987				else
988					continue;
989			}
990			
991			code = target[propertyName];
992			
993			// remove CDATA from <SCRIPT/> tags if it's present
994			if (conf.useScriptTags)
995				code = stripCData(code);
996				
997			// Inject title if the attribute is present
998			if ((target.title || '') != '')
999				params.title = target.title;
1000				
1001			params['brush'] = brushName;
1002			highlighter.init(params);
1003			element = highlighter.getDiv(code);
1004			
1005			// carry over ID
1006			if ((target.id || '') != '')
1007				element.id = target.id;
1008			
1009			target.parentNode.replaceChild(element, target);
1010		}
1011	},
1012
1013	/**
1014	 * Main entry point for the SyntaxHighlighter.
1015	 * @param {Object} params Optional params to apply to all highlighted elements.
1016	 */
1017	all: function(params)
1018	{
1019		attachEvent(
1020			window,
1021			'load',
1022			function() { sh.highlight(params); }
1023		);
1024	}
1025}; // end of sh
1026
1027/**
1028 * Checks if target DOM elements has specified CSS class.
1029 * @param {DOMElement} target Target DOM element to check.
1030 * @param {String} className Name of the CSS class to check for.
1031 * @return {Boolean} Returns true if class name is present, false otherwise.
1032 */
1033function hasClass(target, className)
1034{
1035	return target.className.indexOf(className) != -1;
1036};
1037
1038/**
1039 * Adds CSS class name to the target DOM element.
1040 * @param {DOMElement} target Target DOM element.
1041 * @param {String} className New CSS class to add.
1042 */
1043function addClass(target, className)
1044{
1045	if (!hasClass(target, className))
1046		target.className += ' ' + className;
1047};
1048
1049/**
1050 * Removes CSS class name from the target DOM element.
1051 * @param {DOMElement} target Target DOM element.
1052 * @param {String} className CSS class to remove.
1053 */
1054function removeClass(target, className)
1055{
1056	target.className = target.className.replace(className, '');
1057};
1058
1059/**
1060 * Converts the source to array object. Mostly used for function arguments and 
1061 * lists returned by getElementsByTagName() which aren't Array objects.
1062 * @param {List} source Source list.
1063 * @return {Array} Returns array.
1064 */
1065function toArray(source)
1066{
1067	var result = [];
1068	
1069	for (var i = 0; i < source.length; i++) 
1070		result.push(source[i]);
1071		
1072	return result;
1073};
1074
1075/**
1076 * Splits block of text into lines.
1077 * @param {String} block Block of text.
1078 * @return {Array} Returns array of lines.
1079 */
1080function splitLines(block)
1081{
1082	return block.split(/\r?\n/);
1083}
1084
1085/**
1086 * Generates HTML ID for the highlighter.
1087 * @param {String} highlighterId Highlighter ID.
1088 * @return {String} Returns HTML ID.
1089 */
1090function getHighlighterId(id)
1091{
1092	var prefix = 'highlighter_';
1093	return id.indexOf(prefix) == 0 ? id : prefix + id;
1094};
1095
1096/**
1097 * Finds Highlighter instance by ID.
1098 * @param {String} highlighterId Highlighter ID.
1099 * @return {Highlighter} Returns instance of the highlighter.
1100 */
1101function getHighlighterById(id)
1102{
1103	return sh.vars.highlighters[getHighlighterId(id)];
1104};
1105
1106/**
1107 * Finds highlighter's DIV container.
1108 * @param {String} highlighterId Highlighter ID.
1109 * @return {Element} Returns highlighter's DIV element.
1110 */
1111function getHighlighterDivById(id)
1112{
1113	return document.getElementById(getHighlighterId(id));
1114};
1115
1116/**
1117 * Stores highlighter so that getHighlighterById() can do its thing. Each
1118 * highlighter must call this method to preserve itself.
1119 * @param {Highilghter} highlighter Highlighter instance.
1120 */
1121function storeHighlighter(highlighter)
1122{
1123	sh.vars.highlighters[getHighlighterId(highlighter.id)] = highlighter;
1124};
1125
1126/**
1127 * Looks for a child or parent node which has specified classname.
1128 * Equivalent to jQuery's $(container).find(".className")
1129 * @param {Element} target Target element.
1130 * @param {String} search Class name or node name to look for.
1131 * @param {Boolean} reverse If set to true, will go up the node tree instead of down.
1132 * @return {Element} Returns found child or parent element on null.
1133 */
1134function findElement(target, search, reverse /* optional */)
1135{
1136	if (target == null)
1137		return null;
1138		
1139	var nodes			= reverse != true ? target.childNodes : [ target.parentNode ],
1140		propertyToFind	= { '#' : 'id', '.' : 'className' }[search.substr(0, 1)] || 'nodeName',
1141		expectedValue,
1142		found
1143		;
1144
1145	expectedValue = propertyToFind != 'nodeName'
1146		? search.substr(1)
1147		: search.toUpperCase()
1148		;
1149		
1150	// main return of the found node
1151	if ((target[propertyToFind] || '').indexOf(expectedValue) != -1)
1152		return target;
1153	
1154	for (var i = 0; nodes && i < nodes.length && found == null; i++)
1155		found = findElement(nodes[i], search, reverse);
1156	
1157	return found;
1158};
1159
1160/**
1161 * Looks for a parent node which has specified classname.
1162 * This is an alias to <code>findElement(container, className, true)</code>.
1163 * @param {Element} target Target element.
1164 * @param {String} className Class name to look for.
1165 * @return {Element} Returns found parent element on null.
1166 */
1167function findParentElement(target, className)
1168{
1169	return findElement(target, className, true);
1170};
1171
1172/**
1173 * Finds an index of element in the array.
1174 * @ignore
1175 * @param {Object} searchElement
1176 * @param {Number} fromIndex
1177 * @return {Number} Returns index of element if found; -1 otherwise.
1178 */
1179function indexOf(array, searchElement, fromIndex)
1180{
1181	fromIndex = Math.max(fromIndex || 0, 0);
1182
1183	for (var i = fromIndex; i < array.length; i++)
1184		if(array[i] == searchElement)
1185			return i;
1186	
1187	return -1;
1188};
1189
1190/**
1191 * Generates a unique element ID.
1192 */
1193function guid(prefix)
1194{
1195	return (prefix || '') + Math.round(Math.random() * 1000000).toString();
1196};
1197
1198/**
1199 * Merges two objects. Values from obj2 override values in obj1.
1200 * Function is NOT recursive and works only for one dimensional objects.
1201 * @param {Object} obj1 First object.
1202 * @param {Object} obj2 Second object.
1203 * @return {Object} Returns combination of both objects.
1204 */
1205function merge(obj1, obj2)
1206{
1207	var result = {}, name;
1208
1209	for (name in obj1) 
1210		result[name] = obj1[name];
1211	
1212	for (name in obj2) 
1213		result[name] = obj2[name];
1214		
1215	return result;
1216};
1217
1218/**
1219 * Attempts to convert string to boolean.
1220 * @param {String} value Input string.
1221 * @return {Boolean} Returns true if input was "true", false if input was "false" and value otherwise.
1222 */
1223function toBoolean(value)
1224{
1225	var result = { "true" : true, "false" : false }[value];
1226	return result == null ? value : result;
1227};
1228
1229/**
1230 * Opens up a centered popup window.
1231 * @param {String} url		URL to open in the window.
1232 * @param {String} name		Popup name.
1233 * @param {int} width		Popup width.
1234 * @param {int} height		Popup height.
1235 * @param {String} options	window.open() options.
1236 * @return {Window}			Returns window instance.
1237 */
1238function popup(url, name, width, height, options)
1239{
1240	var x = (screen.width - width) / 2,
1241		y = (screen.height - height) / 2
1242		;
1243		
1244	options +=	', left=' + x + 
1245				', top=' + y +
1246				', width=' + width +
1247				', height=' + height
1248		;
1249	options = options.replace(/^,/, '');
1250
1251	var win = window.open(url, name, options);
1252	win.focus();
1253	return win;
1254};
1255
1256/**
1257 * Adds event handler to the target object.
1258 * @param {Object} obj		Target object.
1259 * @param {String} type		Name of the event.
1260 * @param {Function} func	Handling function.
1261 */
1262function attachEvent(obj, type, func, scope)
1263{
1264	function handler(e)
1265	{
1266		e = e || window.event;
1267		
1268		if (!e.target)
1269		{
1270			e.target = e.srcElement;
1271			e.preventDefault = function()
1272			{
1273				this.returnValue = false;
1274			};
1275		}
1276			
1277		func.call(scope || window, e);
1278	};
1279	
1280	if (obj.attachEvent) 
1281	{
1282		obj.attachEvent('on' + type, handler);
1283	}
1284	else 
1285	{
1286		obj.addEventListener(type, handler, false);
1287	}
1288};
1289
1290/**
1291 * Displays an alert.
1292 * @param {String} str String to display.
1293 */
1294function alert(str)
1295{
1296	window.alert(sh.config.strings.alert + str);
1297};
1298
1299/**
1300 * Finds a brush by its alias.
1301 *
1302 * @param {String} alias		Brush alias.
1303 * @param {Boolean} showAlert	Suppresses the alert if false.
1304 * @return {Brush}				Returns bursh constructor if found, null otherwise.
1305 */
1306function findBrush(alias, showAlert)
1307{
1308	var brushes = sh.vars.discoveredBrushes,
1309		result = null
1310		;
1311	
1312	if (brushes == null) 
1313	{
1314		brushes = {};
1315		
1316		// Find all brushes
1317		for (var brush in sh.brushes) 
1318		{
1319			var info = sh.brushes[brush],
1320				aliases = info.aliases
1321				;
1322			
1323			if (aliases == null) 
1324				continue;
1325			
1326			// keep the brush name
1327			info.brushName = brush.toLowerCase();
1328			
1329			for (var i = 0; i < aliases.length; i++) 
1330				brushes[aliases[i]] = brush;
1331		}
1332		
1333		sh.vars.discoveredBrushes = brushes;
1334	}
1335	
1336	result = sh.brushes[brushes[alias]];
1337
1338	if (result == null && showAlert)
1339		alert(sh.config.strings.noBrush + alias);
1340	
1341	return result;
1342};
1343
1344/**
1345 * Executes a callback on each line and replaces each line with result from the callback.
1346 * @param {Object} str			Input string.
1347 * @param {Object} callback		Callback function taking one string argument and returning a string.
1348 */
1349function eachLine(str, callback)
1350{
1351	var lines = splitLines(str);
1352	
1353	for (var i = 0; i < lines.length; i++)
1354		lines[i] = callback(lines[i], i);
1355		
1356	// include \r to enable copy-paste on windows (ie8) without getting everything on one line
1357	return lines.join('\r\n');
1358};
1359
1360/**
1361 * This is a special trim which only removes first and last empty lines
1362 * and doesn't affect valid leading space on the first line.
1363 * 
1364 * @param {String} str   Input string
1365 * @return {String}      Returns string without empty first and last lines.
1366 */
1367function trimFirstAndLastLines(str)
1368{
1369	return str.replace(/^[ ]*[\n]+|[\n]*[ ]*$/g, '');
1370};
1371
1372/**
1373 * Parses key/value pairs into hash object.
1374 * 
1375 * Understands the following formats:
1376 * - name: word;
1377 * - name: [word, word];
1378 * - name: "string";
1379 * - name: 'string';
1380 * 
1381 * For example:
1382 *   name1: value; name2: [value, value]; name3: 'value'
1383 *   
1384 * @param {String} str    Input string.
1385 * @return {Object}       Returns deserialized object.
1386 */
1387function parseParams(str)
1388{
1389	var match, 
1390		result = {},
1391		arrayRegex = new XRegExp("^\\[(?<values>(.*?))\\]$"),
1392		regex = new XRegExp(
1393			"(?<name>[\\w-]+)" +
1394			"\\s*:\\s*" +
1395			"(?<value>" +
1396				"[\\w-%#]+|" +		// word
1397				"\\[.*?\\]|" +		// [] array
1398				'".*?"|' +			// "" string
1399				"'.*?'" +			// '' string
1400			")\\s*;?",
1401			"g"
1402		)
1403		;
1404
1405	while ((match = regex.exec(str)) != null) 
1406	{
1407		var value = match.value
1408			.replace(/^['"]|['"]$/g, '') // strip quotes from end of strings
1409			;
1410		
1411		// try to parse array value
1412		if (value != null && arrayRegex.test(value))
1413		{
1414			var m = arrayRegex.exec(value);
1415			value = m.values.length > 0 ? m.values.split(/\s*,\s*/) : [];
1416		}
1417		
1418		result[match.name] = value;
1419	}
1420	
1421	return result;
1422};
1423
1424/**
1425 * Wraps each line of the string into <code/> tag with given style applied to it.
1426 * 
1427 * @param {String} str   Input string.
1428 * @param {String} css   Style name to apply to the string.
1429 * @return {String}      Returns input string with each line surrounded by <span/> tag.
1430 */
1431function wrapLinesWithCode(str, css)
1432{
1433	if (str == null || str.length == 0 || str == '\n') 
1434		return str;
1435
1436	str = str.replace(/</g, '&lt;');
1437
1438	// Replace two or more sequential spaces with &nbsp; leaving last space untouched.
1439	str = str.replace(/ {2,}/g, function(m)
1440	{
1441		var spaces = '';
1442		
1443		for (var i = 0; i < m.length - 1; i++)
1444			spaces += sh.config.space;
1445		
1446		return spaces + ' ';
1447	});
1448
1449	// Split each line and apply <span class="...">...</span> to them so that
1450	// leading spaces aren't included.
1451	if (css != null) 
1452		str = eachLine(str, function(line)
1453		{
1454			if (line.length == 0) 
1455				return '';
1456			
1457			var spaces = '';
1458			
1459			line = line.replace(/^(&nbsp;| )+/, function(s)
1460			{
1461				spaces = s;
1462				return '';
1463			});
1464			
1465			if (line.length == 0) 
1466				return spaces;
1467			
1468			return spaces + '<code class="' + css + '">' + line + '</code>';
1469		});
1470
1471	return str;
1472};
1473
1474/**
1475 * Pads number with zeros until it's length is the same as given length.
1476 * 
1477 * @param {Number} number	Number to pad.
1478 * @param {Number} length	Max string length with.
1479 * @return {String}			Returns a string padded with proper amount of '0'.
1480 */
1481function padNumber(number, length)
1482{
1483	var result = number.toString();
1484	
1485	while (result.length < length)
1486		result = '0' + result;
1487	
1488	return result;
1489};
1490
1491/**
1492 * Replaces tabs with spaces.
1493 * 
1494 * @param {String} code		Source code.
1495 * @param {Number} tabSize	Size of the tab.
1496 * @return {String}			Returns code with all tabs replaces by spaces.
1497 */
1498function processTabs(code, tabSize)
1499{
1500	var tab = '';
1501	
1502	for (var i = 0; i < tabSize; i++)
1503		tab += ' ';
1504
1505	return code.replace(/\t/g, tab);
1506};
1507
1508/**
1509 * Replaces tabs with smart spaces.
1510 * 
1511 * @param {String} code    Code to fix the tabs in.
1512 * @param {Number} tabSize Number of spaces in a column.
1513 * @return {String}        Returns code with all tabs replaces with roper amount of spaces.
1514 */
1515function processSmartTabs(code, tabSize)
1516{
1517	var lines = splitLines(code),
1518		tab = '\t',
1519		spaces = ''
1520		;
1521	
1522	// Create a string with 1000 spaces to copy spaces from... 
1523	// It's assumed that there would be no indentation longer than that.
1524	for (var i = 0; i < 50; i++) 
1525		spaces += '                    '; // 20 spaces * 50
1526			
1527	// This function inserts specified amount of spaces in the string
1528	// where a tab is while removing that given tab.
1529	function insertSpaces(line, pos, count)
1530	{
1531		return line.substr(0, pos)
1532			+ spaces.substr(0, count)
1533			+ line.substr(pos + 1, line.length) // pos + 1 will get rid of the tab
1534			;
1535	};
1536
1537	// Go through all the lines and do the 'smart tabs' magic.
1538	code = eachLine(code, function(line)
1539	{
1540		if (line.indexOf(tab) == -1) 
1541			return line;
1542		
1543		var pos = 0;
1544		
1545		while ((pos = line.indexOf(tab)) != -1) 
1546		{
1547			// This is pretty much all there is to the 'smart tabs' logic.
1548			// Based on the position within the line and size of a tab,
1549			// calculate the amount of spaces we need to insert.
1550			var spaces = tabSize - pos % tabSize;
1551			line = insertSpaces(line, pos, spaces);
1552		}
1553		
1554		return line;
1555	});
1556	
1557	return code;
1558};
1559
1560/**
1561 * Performs various string fixes based on configuration.
1562 */
1563function fixInputString(str)
1564{
1565	var br = /<br\s*\/?>|&lt;br\s*\/?&gt;/gi;
1566	
1567	if (sh.config.bloggerMode == true)
1568		str = str.replace(br, '\n');
1569
1570	if (sh.config.stripBrs == true)
1571		str = str.replace(br, '');
1572		
1573	return str;
1574};
1575
1576/**
1577 * Removes all white space at the begining and end of a string.
1578 * 
1579 * @param {String} str   String to trim.
1580 * @return {String}      Returns string without leading and following white space characters.
1581 */
1582function trim(str)
1583{
1584	return str.replace(/^\s+|\s+$/g, '');
1585};
1586
1587/**
1588 * Unindents a block of text by the lowest common indent amount.
1589 * @param {String} str   Text to unindent.
1590 * @return {String}      Returns unindented text block.
1591 */
1592function unindent(str)
1593{
1594	var lines = splitLines(fixInputString(str)),
1595		indents = new Array(),
1596		regex = /^\s*/,
1597		min = 1000
1598		;
1599	
1600	// go through every line and check for common number of indents
1601	for (var i = 0; i < lines.length && min > 0; i++) 
1602	{
1603		var line = lines[i];
1604		
1605		if (trim(line).length == 0) 
1606			continue;
1607		
1608		var matches = regex.exec(line);
1609		
1610		// In the event that just one line doesn't have leading white space
1611		// we can't unindent anything, so bail completely.
1612		if (matches == null) 
1613			return str;
1614			
1615		min = Math.min(matches[0].length, min);
1616	}
1617	
1618	// trim minimum common number of white space from the begining of every line
1619	if (min > 0) 
1620		for (var i = 0; i < lines.length; i++) 
1621			lines[i] = lines[i].substr(min);
1622	
1623	return lines.join('\n');
1624};
1625
1626/**
1627 * Callback method for Array.sort() which sorts matches by
1628 * index position and then by length.
1629 * 
1630 * @param {Match} m1	Left object.
1631 * @param {Match} m2    Right object.
1632 * @return {Number}     Returns -1, 0 or -1 as a comparison result.
1633 */
1634function matchesSortCallback(m1, m2)
1635{
1636	// sort matches by index first
1637	if(m1.index < m2.index)
1638		return -1;
1639	else if(m1.index > m2.index)
1640		return 1;
1641	else
1642	{
1643		// if index is the same, sort by length
1644		if(m1.length < m2.length)
1645			return -1;
1646		else if(m1.length > m2.length)
1647			return 1;
1648	}
1649	
1650	return 0;
1651};
1652
1653/**
1654 * Executes given regular expression on provided code and returns all
1655 * matches that are found.
1656 * 
1657 * @param {String} code    Code to execute regular expression on.
1658 * @param {Object} regex   Regular expression item info from <code>regexList</code> collection.
1659 * @return {Array}         Returns a list of Match objects.
1660 */ 
1661function getMatches(code, regexInfo)
1662{
1663	function defaultAdd(match, regexInfo)
1664	{
1665		return match[0];
1666	};
1667	
1668	var index = 0,
1669		match = null,
1670		matches = [],
1671		func = regexInfo.func ? regexInfo.func : defaultAdd
1672		;
1673	
1674	while((match = regexInfo.regex.exec(code)) != null)
1675	{
1676		var resultMatch = func(match, regexInfo);
1677		
1678		if (typeof(resultMatch) == 'string')
1679			resultMatch = [new sh.Match(resultMatch, match.index, regexInfo.css)];
1680
1681		matches = matches.concat(resultMatch);
1682	}
1683	
1684	return matches;
1685};
1686
1687/**
1688 * Turns all URLs in the code into <a/> tags.
1689 * @param {String} code Input code.
1690 * @return {String} Returns code with </a> tags.
1691 */
1692function processUrls(code)
1693{
1694	var gt = /(.*)((&gt;|&lt;).*)/;
1695	
1696	return code.replace(sh.regexLib.url, function(m)
1697	{
1698		var suffix = '',
1699			match = null
1700			;
1701		
1702		// We include &lt; and &gt; in the URL for the common cases like <http://google.com>
1703		// The problem is that they get transformed into &lt;http://google.com&gt;
1704		// Where as &gt; easily looks like part of the URL string.
1705	
1706		if (match = gt.exec(m))
1707		{
1708			m = match[1];
1709			suffix = match[2];
1710		}
1711		
1712		return '<a href="' + m + '">' + m + '</a>' + suffix;
1713	});
1714};
1715
1716/**
1717 * Finds all <SCRIPT TYPE="syntaxhighlighter" /> elementss.
1718 * @return {Array} Returns array of all found SyntaxHighlighter tags.
1719 */
1720function getSyntaxHighlighterScriptTags()
1721{
1722	var tags = document.getElementsByTagName('script'),
1723		result = []
1724		;
1725	
1726	for (var i = 0; i < tags.length; i++)
1727		if (tags[i].type == 'syntaxhighlighter')
1728			result.push(tags[i]);
1729			
1730	return result;
1731};
1732
1733/**
1734 * Strips <![CDATA[]]> from <SCRIPT /> content because it should be used
1735 * there in most cases for XHTML compliance.
1736 * @param {String} original	Input code.
1737 * @return {String} Returns code without leading <![CDATA[]]> tags.
1738 */
1739function stripCData(original)
1740{
1741	var left = '<![CDATA[',
1742		right = ']]>',
1743		// for some reason IE inserts some leading blanks here
1744		copy = trim(original),
1745		changed = false,
1746		leftLength = left.length,
1747		rightLength = right.length
1748		;
1749	
1750	if (copy.indexOf(left) == 0)
1751	{
1752		copy = copy.substring(leftLength);
1753		changed = true;
1754	}
1755	
1756	var copyLength = copy.length;
1757	
1758	if (copy.indexOf(right) == copyLength - rightLength)
1759	{
1760		copy = copy.substring(0, copyLength - rightLength);
1761		changed = true;
1762	}
1763	
1764	return changed ? copy : original;
1765};
1766
1767
1768/**
1769 * Quick code mouse double click handler.
1770 */
1771function quickCodeHandler(e)
1772{
1773	var target = e.target,
1774		highlighterDiv = findParentElement(target, '.syntaxhighlighter'),
1775		container = findParentElement(target, '.container'),
1776		textarea = document.createElement('textarea'),
1777		highlighter
1778		;
1779
1780	if (!container || !highlighterDiv || findElement(container, 'textarea'))
1781		return;
1782
1783	highlighter = getHighlighterById(highlighterDiv.id);
1784	
1785	// add source class name
1786	addClass(highlighterDiv, 'source');
1787
1788	// Have to go over each line and grab it's text, can't just do it on the
1789	// container because Firefox loses all \n where as Webkit doesn't.
1790	var lines = container.childNodes,
1791		code = []
1792		;
1793	
1794	for (var i = 0; i < lines.length; i++)
1795		code.push(lines[i].innerText || lines[i].textContent);
1796	
1797	// using \r instead of \r or \r\n makes this work equally well on IE, FF and Webkit
1798	code = code.join('\r');
1799
1800    // For Webkit browsers, replace nbsp with a breaking space
1801    code = code.replace(/\u00a0/g, " ");
1802	
1803	// inject <textarea/> tag
1804	textarea.appendChild(document.createTextNode(code));
1805	container.appendChild(textarea);
1806	
1807	// preselect all text
1808	textarea.focus();
1809	textarea.select();
1810	
1811	// set up handler for lost focus
1812	attachEvent(textarea, 'blur', function(e)
1813	{
1814		textarea.parentNode.removeChild(textarea);
1815		removeClass(highlighterDiv, 'source');
1816	});
1817};
1818
1819/**
1820 * Match object.
1821 */
1822sh.Match = function(value, index, css)
1823{
1824	this.value = value;
1825	this.index = index;
1826	this.length = value.length;
1827	this.css = css;
1828	this.brushName = null;
1829};
1830
1831sh.Match.prototype.toString = function()
1832{
1833	return this.value;
1834};
1835
1836/**
1837 * Simulates HTML code with a scripting language embedded.
1838 * 
1839 * @param {String} scriptBrushName Brush name of the scripting language.
1840 */
1841sh.HtmlScript = function(scriptBrushName)
1842{
1843	var brushClass = findBrush(scriptBrushName),
1844		scriptBrush,
1845		xmlBrush = new sh.brushes.Xml(),
1846		bracketsRegex = null,
1847		ref = this,
1848		methodsToExpose = 'getDiv getHtml init'.split(' ')
1849		;
1850
1851	if (brushClass == null)
1852		return;
1853	
1854	scriptBrush = new brushClass();
1855	
1856	for(var i = 0; i < methodsToExpose.length; i++)
1857		// make a closure so we don't lose the name after i changes
1858		(function() {
1859			var name = methodsToExpose[i];
1860			
1861			ref[name] = function()
1862			{
1863				return xmlBrush[name].apply(xmlBrush, arguments);
1864			};
1865		})();
1866	
1867	if (scriptBrush.htmlScript == null)
1868	{
1869		alert(sh.config.strings.brushNotHtmlScript + scriptBrushName);
1870		return;
1871	}
1872	
1873	xmlBrush.regexList.push(
1874		{ regex: scriptBrush.htmlScript.code, func: process }
1875	);
1876	
1877	function offsetMatches(matches, offset)
1878	{
1879		for (var j = 0; j < matches.length; j++) 
1880			matches[j].index += offset;
1881	}
1882	
1883	function process(match, info)
1884	{
1885		var code = match.code,
1886			matches = [],
1887			regexList = scriptBrush.regexList,
1888			offset = match.index + match.left.length,
1889			htmlScript = scriptBrush.htmlScript,
1890			result
1891			;
1892
1893		// add all matches from the code
1894		for (var i = 0; i < regexList.length; i++)
1895		{
1896			result = getMatches(code, regexList[i]);
1897			offsetMatches(result, offset);
1898			matches = matches.concat(result);
1899		}
1900		
1901		// add left script bracket
1902		if (htmlScript.left != null && match.left != null)
1903		{
1904			result = getMatches(match.left, htmlScript.left);
1905			offsetMatches(result, match.index);
1906			matches = matches.concat(result);
1907		}
1908		
1909		// add right script bracket
1910		if (htmlScript.right != null && match.right != null)
1911		{
1912			result = getMatches(match.right, htmlScript.right);
1913			offsetMatches(result, match.index + match[0].lastIndexOf(match.right));
1914			matches = matches.concat(result);
1915		}
1916		
1917		for (var j = 0; j < matches.length; j++)
1918			matches[j].brushName = brushClass.brushName;
1919			
1920		return matches;
1921	}
1922};
1923
1924/**
1925 * Main Highlither class.
1926 * @constructor
1927 */
1928sh.Highlighter = function()
1929{
1930	// not putting any code in here because of the prototype inheritance
1931};
1932
1933sh.Highlighter.prototype = {
1934	/**
1935	 * Returns value of the parameter passed to the highlighter.
1936	 * @param {String} name				Name of the parameter.
1937	 * @param {Object} defaultValue		Default value.
1938	 * @return {Object}					Returns found value or default value otherwise.
1939	 */
1940	getParam: function(name, defaultValue)
1941	{
1942		var result = this.params[name];
1943		return toBoolean(result == null ? defaultValue : result);
1944	},
1945	
1946	/**
1947	 * Shortcut to document.createElement().
1948	 * @param {String} name		Name of the element to create (DIV, A, etc).
1949	 * @return {HTMLElement}	Returns new HTML element.
1950	 */
1951	create: function(name)
1952	{
1953		return document.createElement(name);
1954	},
1955	
1956	/**
1957	 * Applies all regular expression to the code and stores all found
1958	 * matches in the `this.matches` array.
1959	 * @param {Array} regexList		List of regular expressions.
1960	 * @param {String} code			Source code.
1961	 * @return {Array}				Returns list of matches.
1962	 */
1963	findMatches: function(regexList, code)
1964	{
1965		var result = [];
1966		
1967		if (regexList != null)
1968			for (var i = 0; i < regexList.length; i++) 
1969				// BUG: length returns len+1 for array if methods added to prototype chain ([email protected])
1970				if (typeof (regexList[i]) == "object")
1971					result = result.concat(getMatches(code, regexList[i]));
1972		
1973		// sort and remove nested the matches
1974		return this.removeNestedMatches(result.sort(matchesSortCallback));
1975	},
1976	
1977	/**
1978	 * Checks to see if any of the matches are inside of other matches. 
1979	 * This process would get rid of highligted strings inside comments, 
1980	 * keywords inside strings and so on.
1981	 */
1982	removeNestedMatches: function(matches)
1983	{
1984		// Optimized by Jose Prado (http://joseprado.com)
1985		for (var i = 0; i < matches.length; i++) 
1986		{ 
1987			if (matches[i] === null)
1988				continue;
1989			
1990			var itemI = matches[i],
1991				itemIEndPos = itemI.index + itemI.length
1992				;
1993			
1994			for (var j = i + 1; j < matches.length && matches[i] !== null; j++) 
1995			{
1996				var itemJ = matches[j];
1997				
1998				if (itemJ === null) 
1999					continue;
2000				else if (itemJ.index > itemIEndPos) 
2001					break;
2002				else if (itemJ.index == itemI.index && itemJ.length > itemI.length)
2003					matches[i] = null;
2004				else if (itemJ.index >= itemI.index && itemJ.index < itemIEndPos) 
2005					matches[j] = null;
2006			}
2007		}
2008		
2009		return matches;
2010	},
2011	
2012	/**
2013	 * Creates an array containing integer line numbers starting from the 'first-line' param.
2014	 * @return {Array} Returns array of integers.
2015	 */
2016	figureOutLineNumbers: function(code)
2017	{
2018		var lines = [],
2019			firstLine = parseInt(this.getParam('first-line'))
2020			;
2021		
2022		eachLine(code, function(line, index)
2023		{
2024			lines.push(index + firstLine);
2025		});
2026		
2027		return lines;
2028	},
2029	
2030	/**
2031	 * Determines if specified line number is in the highlighted list.
2032	 */
2033	isLineHighlighted: function(lineNumber)
2034	{
2035		var list = this.getParam('highlight', []);
2036		
2037		if (typeof(list) != 'object' && list.push == null) 
2038			list = [ list ];
2039		
2040		return indexOf(list, lineNumber.toString()) != -1;
2041	},
2042	
2043	/**
2044	 * Generates HTML markup for a single line of code while determining alternating line style.
2045	 * @param {Integer} lineNumber	Line number.
2046	 * @param {String} code Line	HTML markup.
2047	 * @return {String}				Returns HTML markup.
2048	 */
2049	getLineHtml: function(lineIndex, lineNumber, code)
2050	{
2051		var classes = [
2052			'line',
2053			'number' + lineNumber,
2054			'index' + lineIndex,
2055			'alt' + (lineNumber % 2 == 0 ? 1 : 2).toString()
2056		];
2057		
2058		if (this.isLineHighlighted(lineNumber))
2059		 	classes.push('highlighted');
2060		
2061		if (lineNumber == 0)
2062			classes.push('break');
2063			
2064		return '<div class="' + classes.join(' ') + '">' + code + '</div>';
2065	},
2066	
2067	/**
2068	 * Generates HTML markup for line number column.
2069	 * @param {String} code			Complete code HTML markup.
2070	 * @param {Array} lineNumbers	Calculated line numbers.
2071	 * @return {String}				Returns HTML markup.
2072	 */
2073	getLineNumbersHtml: function(code, lineNumbers)
2074	{
2075		var html = '',
2076			count = splitLines(code).length,
2077			firstLine = parseInt(this.getParam('first-line')),
2078			pad = this.getParam('pad-line-numbers')
2079			;
2080		
2081		if (pad == true)
2082			pad = (firstLine + count - 1).toString().length;
2083		else if (isNaN(pad) == true)
2084			pad = 0;
2085			
2086		for (var i = 0; i < count; i++)
2087		{
2088			var lineNumber = lineNumbers ? lineNumbers[i] : firstLine + i,
2089				code = lineNumber == 0 ? sh.config.space : padNumber(lineNumber, pad)
2090				;
2091				
2092			html += this.getLineHtml(i, lineNumber, code);
2093		}
2094		
2095		return html;
2096	},
2097	
2098	/**
2099	 * Splits block of text into individual DIV lines.
2100	 * @param {String} code			Code to highlight.
2101	 * @param {Array} lineNumbers	Calculated line numbers.
2102	 * @return {String}				Returns highlighted code in HTML form.
2103	 */
2104	getCodeLinesHtml: function(html, lineNumbers)
2105	{
2106		html = trim(html);
2107		
2108		var lines = splitLines(html),
2109			padLength = this.getParam('pad-line-numbers'),
2110			firstLine = parseInt(this.getParam('first-line')),
2111			html = '',
2112			brushName = this.getParam('brush')
2113			;
2114
2115		for (var i = 0; i < lines.length; i++)
2116		{
2117			var line = lines[i],
2118				indent = /^(&nbsp;|\s)+/.exec(line),
2119				spaces = null,
2120				lineNumber = lineNumbers ? lineNumbers[i] : firstLine + i;
2121				;
2122
2123			if (indent != null)
2124			{
2125				spaces = indent[0].toString();
2126				line = line.substr(spaces.length);
2127				spaces = spaces.replace(' ', sh.config.space);
2128			}
2129
2130			line = trim(line);
2131			
2132			if (line.length == 0)
2133				line = sh.config.space;
2134			
2135			html += this.getLineHtml(
2136				i,
2137				lineNumber, 
2138				(spaces != null ? '<code class="' + brushName + ' spaces">' + spaces + '</code>' : '') + line
2139			);
2140		}
2141		
2142		return html;
2143	},
2144	
2145	/**
2146	 * Returns HTML for the table title or empty string if title is null.
2147	 */
2148	getTitleHtml: function(title)
2149	{
2150		return title ? '<caption>' + title + '</caption>' : '';
2151	},
2152	
2153	/**
2154	 * Finds all matches in the source code.
2155	 * @param {String} code		Source code to process matches in.
2156	 * @param {Array} matches	Discovered regex matches.
2157	 * @return {String} Returns formatted HTML with processed mathes.
2158	 */
2159	getMatchesHtml: function(code, matches)
2160	{
2161		var pos = 0, 
2162			result = '',
2163			brushName = this.getParam('brush', '')
2164			;
2165		
2166		function getBrushNameCss(match)
2167		{
2168			var result = match ? (match.brushName || brushName) : brushName;
2169			return result ? result + ' ' : '';
2170		};
2171		
2172		// Finally, go through the final list of matches and pull the all
2173		// together adding everything in between that isn't a match.
2174		for (var i = 0; i < matches.length; i++) 
2175		{
2176			var match = matches[i],
2177				matchBrushName
2178				;
2179			
2180			if (match === null || match.length === 0) 
2181				continue;
2182			
2183			matchBrushName = getBrushNameCss(match);
2184			
2185			result += wrapLinesWithCode(code.substr(pos, match.index - pos), matchBrushName + 'plain')
2186					+ wrapLinesWithCode(match.value, matchBrushName + match.css)
2187					;
2188
2189			pos = match.index + match.length + (match.offset || 0);
2190		}
2191
2192		// don't forget to add whatever's remaining in the string
2193		result += wrapLinesWithCode(code.substr(pos), getBrushNameCss() + 'plain');
2194
2195		return result;
2196	},
2197	
2198	/**
2199	 * Generates HTML markup for the whole syntax highlighter.
2200	 * @param {String} code Source code.
2201	 * @return {String} Returns HTML markup.
2202	 */
2203	getHtml: function(code)
2204	{
2205		var html = '',
2206			classes = [ 'syntaxhighlighter' ],
2207			tabSize,
2208			matches,
2209			lineNumbers
2210			;
2211		
2212		// process light mode
2213		if (this.getParam('light') == true)
2214			this.params.toolbar = this.params.gutter = false;
2215
2216		className = 'syntaxhighlighter';
2217
2218		if (this.getParam('collapse') == true)
2219			classes.push('collapsed');
2220		
2221		if ((gutter = this.getParam('gutter')) == false)
2222			classes.push('nogutter');
2223
2224		// add custom user style name
2225		classes.push(this.getParam('class-name'));
2226
2227		// add brush alias to the class name for custom CSS
2228		classes.push(this.getParam('brush'));
2229
2230		code = trimFirstAndLastLines(code)
2231			.replace(/\r/g, ' ') // IE lets these buggers through
2232			;
2233
2234		tabSize = this.getParam('tab-size');
2235
2236		// replace tabs with spaces
2237		code = this.getParam('smart-tabs') == true
2238			? processSmartTabs(code, tabSize)
2239			: processTabs(code, tabSize)
2240			;
2241
2242		// unindent code by the common indentation
2243		if (this.getParam('unindent'))
2244			code = unindent(code);
2245
2246		if (gutter)
2247			lineNumbers = this.figureOutLineNumbers(code);
2248		
2249		// find matches in the code using brushes regex list
2250		matches = this.findMatches(this.regexList, code);
2251		// processes found matches into the html
2252		html = this.getMatchesHtml(code, matches);
2253		// finally, split all lines so that they wrap well
2254		html = this.getCodeLinesHtml(html, lineNumbers);
2255
2256		// finally, process the links
2257		if (this.getParam('auto-links'))
2258			html = processUrls(html);
2259		
2260		if (typeof(navigator) != 'undefined' && navigator.userAgent && navigator.userAgent.match(/MSIE/))
2261			classes.push('ie');
2262		
2263		html = 
2264			'<div id="' + getHighlighterId(this.id) + '" class="' + classes.join(' ') + '">'
2265				+ (this.getParam('toolbar') ? sh.toolbar.getHtml(this) : '')
2266				+ '<table border="0" cellpadding="0" cellspacing="0">'
2267					+ this.getTitleHtml(this.getParam('title'))
2268					+ '<tbody>'
2269						+ '<tr>'
2270							+ (gutter ? '<td class="gutter">' + this.getLineNumbersHtml(code) + '</td>' : '')
2271							+ '<td class="code">'
2272								+ '<div class="container">'
2273									+ html
2274								+ '</div>'
2275							+ '</td>'
2276						+ '</tr>'
2277					+ '</tbody>'
2278				+ '</table>'
2279			+ '</div>'
2280			;
2281			
2282		return html;
2283	},
2284	
2285	/**
2286	 * Highlights the code and returns complete HTML.
2287	 * @param {String} code     Code to highlight.
2288	 * @return {Element}        Returns container DIV element with all markup.
2289	 */
2290	getDiv: function(code)
2291	{
2292		if (code === null) 
2293			code = '';
2294		
2295		this.code = code;
2296
2297		var div = this.create('div');
2298
2299		// create main HTML
2300		div.innerHTML = this.getHtml(code);
2301		
2302		// set up click handlers
2303		if (this.getParam('toolbar'))
2304			attachEvent(findElement(div, '.toolbar'), 'click', sh.toolbar.handler);
2305		
2306		if (this.getParam('quick-code'))
2307			attachEvent(findElement(div, '.code'), 'dblclick', quickCodeHandler);
2308		
2309		return div;
2310	},
2311	
2312	/**
2313	 * Initializes the highlighter/brush.
2314	 *
2315	 * Constructor isn't used for initialization so that nothing executes during necessary
2316	 * `new SyntaxHighlighter.Highlighter()` call when setting up brush inheritence.
2317	 *
2318	 * @param {Hash} params Highlighter parameters.
2319	 */
2320	init: function(params)
2321	{
2322		this.id = guid();
2323		
2324		// register this instance in the highlighters list
2325		storeHighlighter(this);
2326		
2327		// local params take precedence over defaults
2328		this.params = merge(sh.defaults, params || {})
2329		
2330		// process light mode
2331		if (this.getParam('light') == true)
2332			this.params.toolbar = this.params.gutter = false;
2333	},
2334	
2335	/**
2336	 * Converts space separated list of keywords into a regular expression string.
2337	 * @param {String} str    Space separated keywords.
2338	 * @return {String}       Returns regular expression string.
2339	 */
2340	getKeywords: function(str)
2341	{
2342		str = str
2343			.replace(/^\s+|\s+$/g, '')
2344			.replace(/\s+/g, '|')
2345			;
2346		
2347		return '\\b(?:' + str + ')\\b';
2348	},
2349	
2350	/**
2351	 * Makes a brush compatible with the `html-script` functionality.
2352	 * @param {Object} regexGroup Object containing `left` and `right` regular expressions.
2353	 */
2354	forHtmlScript: function(regexGroup)
2355	{
2356		var regex = { 'end' : regexGroup.right.source };
2357
2358		if(regexGroup.eof)
2359			regex.end = "(?:(?:" + regex.end + ")|$)";
2360		
2361		this.htmlScript = {
2362			left : { regex: regexGroup.left, css: 'script' },
2363			right : { regex: regexGroup.right, css: 'script' },
2364			code : new XRegExp(
2365				"(?<left>
2365" + regexGroup.left.source + ")" +
2366				"(?<code>.*?)" +
2367				"(?<right>" + regex.end + ")",
2368				"sgi"
2369				)
2370		};
2371	}
2372}; // end of Highlighter
2373
2374return sh;
2375}(); // end of anonymous function
2376
2377// CommonJS
2378typeof(exports) != 'undefined' ? exports.SyntaxHighlighter = SyntaxHighlighter : null;
2379
2380(function() {
2381
2382var sh = SyntaxHighlighter;
2383
2384/**
2385 * Provides functionality to dynamically load only the brushes that a needed to render the current page.
2386 *
2387 * There are two syntaxes that autoload understands. For example:
2388 *
2389 * SyntaxHighlighter.autoloader(
2390 *     [ 'applescript',          'Scripts/shBrushAppleScript.js' ],
2391 *     [ 'actionscript3', 'as3', 'Scripts/shBrushAS3.js' ]
2392 * );
2393 *
2394 * or a more easily comprehendable one:
2395 *
2396 * SyntaxHighlighter.autoloader(
2397 *     'applescript       Scripts/shBrushAppleScript.js',
2398 *     'actionscript3 as3 Scripts/shBrushAS3.js'
2399 * );
2400 */
2401sh.autoloader = function()
2402{
2403	var list = arguments,
2404		elements = sh.findElements(),
2405		brushes = {},
2406		scripts = {},
2407		all = SyntaxHighlighter.all,
2408		allCalled = false,
2409		allParams = null,
2410		i
2411		;
2412
2413	SyntaxHighlighter.all = function(params)
2414	{
2415		allParams = params;
2416		allCalled = true;
2417	};
2418
2419	function addBrush(aliases, url)
2420	{
2421		for (var i = 0; i < aliases.length; i++)
2422			brushes[aliases[i]] = url;
2423	};
2424
2425	function getAliases(item)
2426	{
2427		return item.pop
2428			? item
2429			: item.split(/\s+/)
2430			;
2431	}
2432
2433	// create table of aliases and script urls
2434	for (i = 0; i < list.length; i++)
2435	{
2436		var aliases = getAliases(list[i]),
2437			url = aliases.pop()
2438			;
2439
2440		addBrush(aliases, url);
2441	}
2442
2443	// dynamically add <script /> tags to the document body
2444	for (i = 0; i < elements.length; i++)
2445	{
2446		var url = brushes[elements[i].params.brush];
2447
2448		if(url && scripts[url] === undefined)
2449		{
2450			if(elements[i].params['html-script'] === 'true')
2451			{
2452				if(scripts[brushes['xml']] === undefined) {
2453					loadScript(brushes['xml']);
2454					scripts[url] = false;
2455				}
2456			}
2457
2458			scripts[url] = false;
2459			loadScript(url);
2460		}
2461	}
2462
2463	function loadScript(url)
2464	{
2465		var script = document.createElement('script'),
2466			done = false
2467			;
2468
2469		script.src = url;
2470		script.type = 'text/javascript';
2471		script.language = 'javascript';
2472		script.onload = script.onreadystatechange = function()
2473		{
2474			if (!done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete'))
2475			{
2476				done = true;
2477				scripts[url] = true;
2478				checkAll();
2479
2480				// Handle memory leak in IE
2481				script.onload = script.onreadystatechange = null;
2482				script.parentNode.removeChild(script);
2483			}
2484		};
2485
2486		// sync way of adding script tags to the page
2487		document.body.appendChild(script);
2488	};
2489
2490	function checkAll()
2491	{
2492		for(var url in scripts)
2493			if (scripts[url] == false)
2494				return;
2495
2496		if (allCalled)
2497			SyntaxHighlighter.highlight(allParams);
2498	};
2499};
2500
2501})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.