﻿/**
 * Code Syntax Highlighter.
 * Version 1.3.0
 * Copyright (C) 2004 Alex Gorbatchev.
 * http://www.dreamprojections.com/syntaxhighlighter/
 * 
 * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General 
 * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) 
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to 
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 */

/**
 * Substantial modifications made by Bernard Sumption (berniecode.com). I claim no copyright on my modifications so
 * the above notice stands. In particular, the script now replaces the element
 */

//
// create namespaces
//
function SyntaxHighlighter() {}

SyntaxHighlighter.Brushes = new Object();


// Class to represent RE match regions
SyntaxHighlighter.Match = function(value, index, css)
{
    this.value = value;
    this.index = index;
    this.length = value.length;
    this.css = css;
}


// static callback for the match sorting
SyntaxHighlighter.SortCallback = function(m1, m2)
{
    // sort matches by index first
    if (m1.index < m2.index)
        return -1;
    else if (m1.index > m2.index)
        return 1;
    else
    {
        // if index is the same, sort by length
        if (m1.length < m2.length)
            return -1;
        else if (m1.length > m2.length)
            return 1;
    }
    return 0;
}

// gets a list of all matches for a given regular expression
SyntaxHighlighter.prototype.GetMatches = function(regex, css, code)
{
    var index = 0;
    var match = null;

    while ((match = regex.exec(code)) != null)
    {
        this.matches[this.matches.length] = new SyntaxHighlighter.Match(match[0], match.index, css);
    }
}



// checks if one match is inside any other match
SyntaxHighlighter.prototype.IsInside = function(match)
{
    if (match == null || match.length == 0)
        return;

    for (var i = 0; i < this.matches.length; i++)
    {
        var c = this.matches[i];

        if (c == null)
            continue;

        if ((match.index > c.index) && (match.index <= c.index + c.length))
            return true;
    }

    return false;
}

SyntaxHighlighter.prototype.ProcessRegexList = function(code)
{
    for (var i = 0; i < this.regexList.length; i++)
        this.GetMatches(this.regexList[i].regex, this.regexList[i].css, code);
}

// get the text inside an element
SyntaxHighlighter.prototype.GetInnerText = function(element)
{
    // for browsers that support it, this is faster
    if (element.innerText) {
        return element.innerText;
    }
    // otherwise do it the hard way
    var innerText = "";
    for (var i = 0; i < element.childNodes.length; i++) {
        if (element.childNodes[i].nodeType == 3) {
            innerText += element.childNodes[i].nodeValue;
        } else {
            innerText + this.GetInnerText(element.childNodes[i]);
        }
    }
    return innerText;
}

SyntaxHighlighter.prototype.Highlight = function(element)
{
    // get the code to highlight
    var code = this.GetInnerText(element);
    
    element.originalText = code;
    
    // make platform linebreaks consistent
    code = code.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
    
    // trim empty lines at start and end
    code = code.replace(/^\s*\n/g, "").replace(/\n\s*$/g, "");

    // remove content from the element
    while (element.childNodes.length > 0) {
        element.removeChild(element.childNodes[0]);
    }

    this.matches = new Array();

    this.ProcessRegexList(code);

    if (this.matches.length == 0)
    {
        this.AddText(element, code);
        return;
    }

    var result = "";

    // sort the matches
    this.matches = this.matches.sort(SyntaxHighlighter.SortCallback);

    // The following loop checks to see if any of the matches are inside
    // of other matches. This process would get rid of highligting strings
    // inside comments, keywords inside strings and so on.
    for (var i = 0; i < this.matches.length; i++) {
        if (this.IsInside(this.matches[i])) {
            this.matches[i] = null;
        }
    }

    // Finally, go through the final list of matches and pull the all
    // together adding everything in between that isn't a match.
    var pos = 0;
    var span;
    for (var i = 0; i < this.matches.length; i++)
    {
        var match = this.matches[i];

        if (match == null || match.length == 0)
            continue;

        // add the text between the end of the last match and the start of this one
        this.AddText(element, code.substring(pos, match.index));
        //element.appendChild(document.createTextNode(code.substring(pos, match.index)));

        // add the match itself, highlighted
        this.AddText(element, match.value, match.css);

        pos = match.index + match.length;
    }
    this.AddText(element, code.substring(pos))
}

SyntaxHighlighter.prototype.AddText = function(element, text, css) {
    var el;
    if (css) {
        el = document.createElement("SPAN");
        el.className = css;
        element.appendChild(el);
        element = el;
    }
    var lines = text.split("\n");
    
    for (var i = 0; i < lines.length; i++) {
        element.appendChild(document.createTextNode(lines[i]));
        if (i < lines.length - 1) {
            if (lines[i] == "") {
                element.appendChild(document.createTextNode(" "));
            }
            element.appendChild(document.createElement("BR"));
        }
    }
}

SyntaxHighlighter.prototype.GetKeywords = function(str)
{
    return '\\b' + str.replace(/ /g, '\\b|\\b') + '\\b';
}

// highlights all elements with a highlight attribute
SyntaxHighlighter.HighlightAll = function()
{
    var elementsList = document.getElementsByTagName("PRE");
    var elements = [];
    for (var i=0; i<elementsList.length; i++) {
    	elements[i] = elementsList[i];
    }

    for (var i = 0; i < elements.length; i++) {
		var element = elements[i];
		var language = element.getAttribute('highlight');
		if (language) {
			// if this is a codelisting element, create a new subelement for the highlighted code
			var codelisting = element.getAttribute('codelisting');
			if (codelisting) {
				var listing = document.createElement("P");
				listing.className = "codelistingtitle";
				listing.appendChild(document.createTextNode(codelisting + " "));
				
				var link = document.createElement("A");
				link.innerHTML = 'open';
				link.href = "#";
				link.onclick = SyntaxHighlighter.CodeListingToggle;
				listing.appendChild(link);
				
				var container = document.createElement(element.nodeName);
				link.codeElement = container;
				
				container.style.display = 'none';
				while (element.childNodes.length > 0) {
					var sub = element.childNodes[0];
					sub.parentNode.removeChild(sub);
					container.appendChild(sub);
				}
				element.appendChild(listing);
				element.appendChild(container);
				
				element = container;
            }

			if (SyntaxHighlighter.Brushes[language] == null)
				continue;

			var highlighter = new SyntaxHighlighter.Brushes[language]();
			highlighter.Highlight(element);
			element.className = element.className + " " + language;
        }
    }
}

SyntaxHighlighter.CodeListingToggle = function() {
	if (this.codeElement.style.display == 'none') {
		this.codeElement.style.display = '';
		this.innerHTML = 'close';
	} else {
		this.codeElement.style.display = 'none';
		this.innerHTML = 'open';
	}
	return false;
}


SyntaxHighlighter.Brushes.javascript = function()
{
	var keywords = 'abstract boolean break byte case catch char class const continue debugger ' +
				   'default delete do double else enum export extends false final finally float ' +
				   'for function goto if implements import in instanceof int interface long native ' +
				   'new null package private protected public return short static super switch ' +
				   'synchronized this throw throws transient true try typeof var void volatile while with';

	this.regexList = [
	{ regex: new RegExp('//.*$', 'gm'),							css: 'comment' },			// one line comments
	{ regex: new RegExp('/\\*[\\s\\S]*?\\*/', 'g'),				css: 'comment' },			// multiline comments
	{ regex: new RegExp('"(?:[^"\n]|[\"])*?"', 'g'),			css: 'string' },			// double quoted strings
	{ regex: new RegExp("'(?:[^'\n]|[\'])*?'", 'g'),			css: 'string' },			// single quoted strings
	{ regex: new RegExp('^\\s*#.*', 'gm'),						css: 'preprocessor' },		// preprocessor tags like #region and #endregion
	{ regex: new RegExp(this.GetKeywords(keywords), 'gm'),		css: 'keyword' }			// keywords
			];

}
SyntaxHighlighter.Brushes.javascript.prototype = new SyntaxHighlighter();

SyntaxHighlighter.Brushes.java = SyntaxHighlighter.Brushes.javascript;


SyntaxHighlighter.Brushes.xml = function() {}

SyntaxHighlighter.Brushes.xml.prototype = new SyntaxHighlighter();
SyntaxHighlighter.Brushes.xml.Aliases = ['xml', 'xhtml', 'xslt', 'html', 'xhtml'];

SyntaxHighlighter.Brushes.xml.prototype.ProcessRegexList = function()
{
	function push(array, value)
	{
		array[array.length] = value;
	}

	/* If only there was a way to get index of a group within a match, the whole XML
		could be matched with the expression looking something like that:
	
		(<!\[CDATA\[\s*.*\s*\]\]>)
		| (<!--\s*.*\s*?-->)
		| (<)*(\w+)*\s*(\w+)\s*=\s*(".*?"|'.*?'|\w+)(/*>)*
		| (</?)(.*?)(/?>)
	 */
	var index = 0;
	var match = null;
	var regex = null;

	// Match CDATA in the following format <![ ... [ ... ]]>
	// <\!\[[\w\s]*?\[(.|\s)*?\]\]>
	this.GetMatches(new RegExp('<\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\]>', 'gm'), 'cdata');

	// Match comments
	// <!--\s*.*\s*?-->
	this.GetMatches(new RegExp('<!--\\s*.*\\s*?-->', 'gm'), 'comments');

	// Match attributes and their values
	// (\w+)\s*=\s*(".*?"|\'.*?\'|\w+)*
	regex = new RegExp('([\\w-\.]+)\\s*=\\s*(".*?"|\'.*?\'|\\w+)*', 'gm');
	while ((match = regex.exec(this.code)) != null)
	{
		push(this.matches, new SyntaxHighlighter.Match(match[1], match.index, 'attribute'));

		// if xml is invalid and attribute has no property value, ignore it
		if (match[2] != undefined)
		{
			push(this.matches, new SyntaxHighlighter.Match(match[2], match.index + match[0].indexOf(match[2]), 'attribute-value'));
		}
	}

	// Match opening and closing tag brackets
	// </*\?*(?!\!)|/*\?*>
	this.GetMatches(new RegExp('</*\\?*(?!\\!)|/*\\?*>', 'gm'), 'tag');

	// Match tag names
	// </*\?*\s*(\w+)
	regex = new RegExp('</*\\?*\\s*([\\w-\.]+)', 'gm');
	while ((match = regex.exec(this.code)) != null)
	{
		push(this.matches, new SyntaxHighlighter.Match(match[1], match.index + match[0].indexOf(match[1]), 'tag-name'));
	}
}



