/**

* Prism: Lightweight, robust, elegant syntax highlighting
* MIT license http://www.opensource.org/licenses/mit-license.php/
* @author Lea Verou http://lea.verou.me
*/

(function(){

// Private helper vars var lang = /blang(?:uage)?-(?!*)(w+)b/i;

var _ = self.Prism = {

util: {
        type: function (o) { 
                return Object.prototype.toString.call(o).match(/\[object (\w+)\]/)[1];
        },

        // Deep clone a language definition (e.g. to extend it)
        clone: function (o) {
                var type = _.util.type(o);

                switch (type) {
                        case 'Object':
                                var clone = {};

                                for (var key in o) {
                                        if (o.hasOwnProperty(key)) {
                                                clone[key] = _.util.clone(o[key]);
                                        }
                                }

                                return clone;

                        case 'Array':
                                return o.slice();
                }

                return o;
        }
},

languages: {
        extend: function (id, redef) {
                var lang = _.util.clone(_.languages[id]);

                for (var key in redef) {
                        lang[key] = redef[key];
                }

                return lang;
        },

        // Insert a token before another token in a language literal
        insertBefore: function (inside, before, insert, root) {
                root = root || _.languages;
                var grammar = root[inside];
                var ret = {};

                for (var token in grammar) {

                        if (grammar.hasOwnProperty(token)) {

                                if (token == before) {

                                        for (var newToken in insert) {

                                                if (insert.hasOwnProperty(newToken)) {
                                                        ret[newToken] = insert[newToken];
                                                }
                                        }
                                }

                                ret[token] = grammar[token];
                        }
                }

                return root[inside] = ret;
        },

        // Traverse a language definition with Depth First Search
        DFS: function(o, callback) {
                for (var i in o) {
                        callback.call(o, i, o[i]);

                        if (_.util.type(o) === 'Object') {
                                _.languages.DFS(o[i], callback);
                        }
                }
        }
},

highlightAll: function(async, callback) {
        var elements = document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code');

        for (var i=0, element; element = elements[i++];) {
                _.highlightElement(element, async === true, callback);
        }
},

highlightElement: function(element, async, callback) {
        // Find language
        var language, grammar, parent = element;

        while (parent && !lang.test(parent.className)) {
                parent = parent.parentNode;
        }

        if (parent) {
                language = (parent.className.match(lang) || [,''])[1];
                grammar = _.languages[language];
        }

        if (!grammar) {
                return;
        }

        // Set language on the element, if not present
        element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;

        // Set language on the parent, for styling
        parent = element.parentNode;

        if (/pre/i.test(parent.nodeName)) {
                parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language; 
        }

        var code = element.textContent;

        if(!code) {
                return;
        }

        code = code.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/\u00a0/g, ' ');

        var env = {
                element: element,
                language: language,
                grammar: grammar,
                code: code
        };

        _.hooks.run('before-highlight', env);

        if (async && self.Worker) {
                var worker = new Worker(_.filename);    

                worker.onmessage = function(evt) {
                        env.highlightedCode = Token.stringify(JSON.parse(evt.data), language);

                        _.hooks.run('before-insert', env);

                        env.element.innerHTML = env.highlightedCode;

                        callback && callback.call(env.element);
                        _.hooks.run('after-highlight', env);
                };

                worker.postMessage(JSON.stringify({
                        language: env.language,
                        code: env.code
                }));
        }
        else {
                env.highlightedCode = _.highlight(env.code, env.grammar, env.language)

                _.hooks.run('before-insert', env);

                env.element.innerHTML = env.highlightedCode;

                callback && callback.call(element);

                _.hooks.run('after-highlight', env);
        }
},

highlight: function (text, grammar, language) {
        return Token.stringify(_.tokenize(text, grammar), language);
},

tokenize: function(text, grammar, language) {
        var Token = _.Token;

        var strarr = [text];

        var rest = grammar.rest;

        if (rest) {
                for (var token in rest) {
                        grammar[token] = rest[token];
                }

                delete grammar.rest;
        }

        tokenloop: for (var token in grammar) {
                if(!grammar.hasOwnProperty(token) || !grammar[token]) {
                        continue;
                }

                var pattern = grammar[token], 
                        inside = pattern.inside,
                        lookbehind = !!pattern.lookbehind,
                        lookbehindLength = 0;

                pattern = pattern.pattern || pattern;

                for (var i=0; i<strarr.length; i++) { // Don’t cache length as it changes during the loop

                        var str = strarr[i];

                        if (strarr.length > text.length) {
                                // Something went terribly wrong, ABORT, ABORT!
                                break tokenloop;
                        }

                        if (str instanceof Token) {
                                continue;
                        }

                        pattern.lastIndex = 0;

                        var match = pattern.exec(str);

                        if (match) {
                                if(lookbehind) {
                                        lookbehindLength = match[1].length;
                                }

                                var from = match.index - 1 + lookbehindLength,
                                    match = match[0].slice(lookbehindLength),
                                    len = match.length,
                                    to = from + len,
                                        before = str.slice(0, from + 1),
                                        after = str.slice(to + 1); 

                                var args = [i, 1];

                                if (before) {
                                        args.push(before);
                                }

                                var wrapped = new Token(token, inside? _.tokenize(match, inside) : match);

                                args.push(wrapped);

                                if (after) {
                                        args.push(after);
                                }

                                Array.prototype.splice.apply(strarr, args);
                        }
                }
        }

        return strarr;
},

hooks: {
        all: {},

        add: function (name, callback) {
                var hooks = _.hooks.all;

                hooks[name] = hooks[name] || [];

                hooks[name].push(callback);
        },

        run: function (name, env) {
                var callbacks = _.hooks.all[name];

                if (!callbacks || !callbacks.length) {
                        return;
                }

                for (var i=0, callback; callback = callbacks[i++];) {
                        callback(env);
                }
        }
}

};

var Token = _.Token = function(type, content) {

this.type = type;
this.content = content;

};

Token.stringify = function(o, language, parent) {

if (typeof o == 'string') {
        return o;
}

if (Object.prototype.toString.call(o) == '[object Array]') {
        return o.map(function(element) {
                return Token.stringify(element, language, o);
        }).join('');
}

var env = {
        type: o.type,
        content: Token.stringify(o.content, language, parent),
        tag: 'span',
        classes: ['token', o.type],
        attributes: {},
        language: language,
        parent: parent
};

if (env.type == 'comment') {
        env.attributes['spellcheck'] = 'true';
}

_.hooks.run('wrap', env);

var attributes = '';

for (var name in env.attributes) {
        attributes += name + '="' + (env.attributes[name] || '') + '"';
}

return '<' + env.tag + ' class="' + env.classes.join(' ') + '" ' + attributes + '>' + env.content + '</' + env.tag + '>';

};

if (!self.document) {

// In worker
self.addEventListener('message', function(evt) {
        var message = JSON.parse(evt.data),
            lang = message.language,
            code = message.code;

        self.postMessage(JSON.stringify(_.tokenize(code, _.languages[lang])));
        self.close();
}, false);

return;

}

})();; Prism.languages.markup = {

'comment': /&lt;!--[\w\W]*?-->/g,
'prolog': /&lt;\?.+?\?>/,
'doctype': /&lt;!DOCTYPE.+?>/,
'cdata': /&lt;!\[CDATA\[[\w\W]*?]]>/i,
'tag': {
        pattern: /&lt;\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|\w+))?\s*)*\/?>/gi,
        inside: {
                'tag': {
                        pattern: /^&lt;\/?[\w:-]+/i,
                        inside: {
                                'punctuation': /^&lt;\/?/,
                                'namespace': /^[\w-]+?:/
                        }
                },
                'attr-value': {
                        pattern: /=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi,
                        inside: {
                                'punctuation': /=|>|"/g
                        }
                },
                'punctuation': /\/?>/g,
                'attr-name': {
                        pattern: /[\w:-]+/g,
                        inside: {
                                'namespace': /^[\w-]+?:/
                        }
                }

        }
},
'entity': /&amp;#?[\da-z]{1,8};/gi

};

// Plugin to make entity title show the real entity, idea by Roman Komarov Prism.hooks.add('wrap', function(env) {

if (env.type === 'entity') {
        env.attributes['title'] = env.content.replace(/&amp;/, '&');
}

});; Prism.languages.css = {

'comment': /\/\*[\w\W]*?\*\//g,
'atrule': {
        pattern: /@[\w-]+?.*?(;|(?=\s*{))/gi,
        inside: {
                'punctuation': /[;:]/g
        }
},
'url': /url\((["']?).*?\1\)/gi,
'selector': /[^\{\}\s][^\{\};]*(?=\s*\{)/g,
'property': /(\b|\B)[\w-]+(?=\s*:)/ig,
'string': /("|')(\\?.)*?\1/g,
'important': /\B!important\b/gi,
'ignore': /&(lt|gt|amp);/gi,
'punctuation': /[\{\};:]/g

};

if (Prism.languages.markup) {

Prism.languages.insertBefore('markup', 'tag', {
        'style': {
                pattern: /(&lt;|<)style[\w\W]*?(>|&gt;)[\w\W]*?(&lt;|<)\/style(>|&gt;)/ig,
                inside: {
                        'tag': {
                                pattern: /(&lt;|<)style[\w\W]*?(>|&gt;)|(&lt;|<)\/style(>|&gt;)/ig,
                                inside: Prism.languages.markup.tag.inside
                        },
                        rest: Prism.languages.css
                }
        }
});

}; Prism.languages.clike = {

'comment': {
        pattern: /(^|[^\\])(\/\*[\w\W]*?\*\/|(^|[^:])\/\/.*?(\r?\n|$))/g,
        lookbehind: true
},
'string': /("|')(\\?.)*?\1/g,
'class-name': {
        pattern: /((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/ig,
        lookbehind: true,
        inside: {
                punctuation: /(\.|\\)/
        }
},
'keyword': /\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,
'boolean': /\b(true|false)\b/g,
'function': {
        pattern: /[a-z0-9_]+\(/ig,
        inside: {
                punctuation: /\(/
        }
},
'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,
'operator': /[-+]{1,2}|!|&lt;=?|>=?|={1,3}|(&amp;){1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,
'ignore': /&(lt|gt|amp);/gi,
'punctuation': /[{}[\];(),.:]/g

}; ; Prism.languages.javascript = Prism.languages.extend('clike', {

'keyword': /\b(var|let|if|else|while|do|for|return|in|instanceof|function|new|with|typeof|try|throw|catch|finally|null|break|continue)\b/g,
'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g

});

Prism.languages.insertBefore('javascript', 'keyword', {

'regex': {
        pattern: /(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,
        lookbehind: true
}

});

if (Prism.languages.markup) {

Prism.languages.insertBefore('markup', 'tag', {
        'script': {
                pattern: /(&lt;|<)script[\w\W]*?(>|&gt;)[\w\W]*?(&lt;|<)\/script(>|&gt;)/ig,
                inside: {
                        'tag': {
                                pattern: /(&lt;|<)script[\w\W]*?(>|&gt;)|(&lt;|<)\/script(>|&gt;)/ig,
                                inside: Prism.languages.markup.tag.inside
                        },
                        rest: Prism.languages.javascript
                }
        }
});

} ; Prism.languages.coffeescript = Prism.languages.extend('javascript', {

'block-comment': /([#]{3}\s*\r?\n(.*\s*\r*\n*)\s*?\r?\n[#]{3})/g,
'comment': /(\s|^)([#]{1}[^#^\r^\n]{2,}?(\r?\n|$))/g,
'keyword': /\b(this|window|delete|class|extends|namespace|extend|ar|let|if|else|while|do|for|each|of|return|in|instanceof|new|with|typeof|try|catch|finally|null|undefined|break|continue)\b/g

});

Prism.languages.insertBefore('coffeescript', 'keyword', {

'function': {
        pattern: /[a-z|A-z]+\s*[:|=]\s*(\([.|a-z\s|,|:|{|}|\"|\'|=]*\))?\s*-&gt;/gi,
        inside: {
                'function-name': /[_?a-z-|A-Z-]+(\s*[:|=])| @[_?$?a-z-|A-Z-]+(\s*)| /g,
                'operator': /[-+]{1,2}|!|=?&lt;|=?&gt;|={1,2}|(&amp;){1,2}|\|?\||\?|\*|\//g
        }
},
'attr-name': /[_?a-z-|A-Z-]+(\s*:)| @[_?$?a-z-|A-Z-]+(\s*)| /g

}); ; Prism.languages.scss = Prism.languages.extend('css', {

'comment': {
        pattern: /(^|[^\\])(\/\*[\w\W]*?\*\/|\/\/.*?(\r?\n|$))/g,
        lookbehind: true
},
// aturle is just the @***, not the entire rule (to highlight var & stuffs)
// + add ability to highlight number & unit for media queries
'atrule': /@[\w-]+(?=\s+(\(|\{|;))/gi,
// url, compassified
'url': /([-a-z]+-)*url(?=\()/gi,
// CSS selector regex is not appropriate for Sass
// since there can be lot more things (var, @ directive, nesting..)
// a selector must start at the end of a property or after a brace (end of other rules or nesting)
// it can contain some caracters that aren't used for defining rules or end of selector, & (parent selector), or interpolated variable
// the end of a selector is found when there is no rules in it ( {} or {\s}) or if there is a property (because an interpolated var
// can "pass" as a selector- e.g: proper#{$erty})
// this one was ard to do, so please be careful if you edit this one :)
'selector': /([^@;\{\}\(\)]?([^@;\{\}\(\)]|&amp;|\#\{\$[-_\w]+\})+)(?=\s*\{(\}|\s|[^\}]+(:|\{)[^\}]+))/gm

});

Prism.languages.insertBefore('scss', 'atrule', {

'keyword': /@(if|else if|else|for|each|while|import|extend|debug|warn|mixin|include|function|return)|(?=@for\s+\$[-_\w]+\s)+from/i

});

Prism.languages.insertBefore('scss', 'property', {

// var and interpolated vars
'variable': /((\$[-_\w]+)|(#\{\$[-_\w]+\}))/i

});

Prism.languages.insertBefore('scss', 'ignore', {

'placeholder': /%[-_\w]+/i,
'statement': /\B!(default|optional)\b/gi,
'boolean': /\b(true|false)\b/g,
'null': /\b(null)\b/g,
'operator': /\s+([-+]{1,2}|={1,2}|!=|\|?\||\?|\*|\/|\%)\s+/g

}); ; Prism.languages.bash = Prism.languages.extend('clike', {

'comment': {
        pattern: /(^|[^"{\\])(#.*?(\r?\n|$))/g,
        lookbehind: true
},
'string': {
        //allow multiline string
        pattern: /("|')(\\?[\s\S])*?\1/g,
        inside: {
                //'property' class reused for bash variables
                'property': /\$([a-zA-Z0-9_#\?\-\*!@]+|\{[^\}]+\})/g
        }
},
'keyword': /\b(if|then|else|elif|fi|for|break|continue|while|in|case|function|select|do|done|until|echo|exit|return|set|declare)\b/g

});

Prism.languages.insertBefore('bash', 'keyword', {

//'property' class reused for bash variables
'property': /\$([a-zA-Z0-9_#\?\-\*!@]+|\{[^}]+\})/g

}); Prism.languages.insertBefore('bash', 'comment', {

//shebang must be before comment, 'important' class from css reused
'important': /(^#!\s*\/bin\/bash)|(^#!\s*\/bin\/sh)/g

}); ; Prism.languages.c = Prism.languages.extend('clike', {

'keyword': /\b(asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/g,
'operator': /[-+]{1,2}|!=?|&lt;{1,2}=?|&gt;{1,2}=?|\-&gt;|={1,2}|\^|~|%|(&amp;){1,2}|\|?\||\?|\*|\//g

});

Prism.languages.insertBefore('c', 'keyword', {

//property class reused for macro statements
'property': /#\s*[a-zA-Z]+/g

}); ; Prism.hooks.add('after-highlight', function (env) {

// works only for <code> wrapped inside <pre data-line-numbers> (not inline)
var pre = env.element.parentNode;
if (!pre || !/pre/i.test(pre.nodeName) || pre.className.indexOf('line-numbers') === -1) {
        return;
}

var linesNum = (1 + env.code.split('\n').length);
var lineNumbersWrapper;

lines = new Array(linesNum);
lines = lines.join('<span></span>');

lineNumbersWrapper = document.createElement('span');
lineNumbersWrapper.className = 'line-numbers-rows';
lineNumbersWrapper.innerHTML = lines;

if (pre.hasAttribute('data-start')) {
        pre.style.counterReset = 'linenumber ' + (parseInt(pre.getAttribute('data-start'), 10) - 1);
}

env.element.appendChild(lineNumbersWrapper);

});;