	//
	//
	//  SYGHT - JavaScript DOM Text Highlight Library
	//
	//  Copyright 2007, Pavel Simakov, 
	//  http://www.softwaresecretweapons.com
	//
	//

	//
	// Based on original work from http://kryogenix.org/code/browser/searchhi/
	//
	// Modified 20021006 to fix query string parsing and add case insensitivity 
	// Modified 20070316 to stop highlighting inside nosearchhi nodes
	//
	function oySyghtHighlight(cookieName, classNameOn, classNameOff) {
		this.cookieName = cookieName;
		this.classNameOn = classNameOn;
		this.classNameOff = classNameOff;

		this.active = false;
		this.on = true;
	}

	//
	// returns true if applyTerms() was called at least once
	//
	oySyghtHighlight.prototype.isActive = function(){
		return this.active;
	}

	//
	// returns true if applyTerms() was called 
	//
	// and highlight is ON
	//
	oySyghtHighlight.prototype.isOn = function(){
		return this.active && this.on;
	}


	//
	// returns true if applyTerms() was called 
	//
	// and highlight is OFF
	//
	oySyghtHighlight.prototype.isOff = function(){
		return this.active && !this.on;
	}


	//
	// invert styles for all highlighted nodes
	//
	oySyghtHighlight.prototype.toggle = function () {
		if (!this.active){
			return;
		}

		var all = document.getElementsByTagName("span");
		for (var i=0; i < all.length; i++){
			var elem = all[i];

			if (this.on){
				if (elem && elem.className == this.classNameOn){
					elem.className = this.classNameOff;
				}
			} else {
				if (elem && elem.className == this.classNameOff){
					elem.className = this.classNameOn;
				}
			}
		}

		this.on = !this.on;
	}


	/**
	 * Sets a Cookie with the given name and value.
	 *
	 * name       Name of the cookie
	 * value      Value of the cookie
	 * [expires]  Expiration date of the cookie (default: end of current session)
	 * [path]     Path where the cookie is valid (default: path of calling document)
	 * [domain]   Domain where the cookie is valid
	 *              (default: domain of calling document)
	 * [secure]   Boolean value indicating if the cookie transmission requires a
	 *              secure transmission
	 */
	oySyghtHighlight.prototype.setCookie = function (name, value, expires, path, domain, secure) {
		document.cookie= name + "=" + escape(value) +
			((expires) ? "; expires=" + expires.toGMTString() : "") +
			((path) ? "; path=" + path : "") +
			((domain) ? "; domain=" + domain : "") +
			((secure) ? "; secure" : "");
	} 

	/**
	 * Gets the value of the specified cookie.
	 *
	 * name  Name of the desired cookie.
	 *
	 * Returns a string containing value of specified cookie,
	 *   or null if cookie does not exist.
	 */
	oySyghtHighlight.prototype.getCookie = function (name) {
		var dc = document.cookie;
		var prefix = name + "=";
		var begin = dc.indexOf("; " + prefix);
		if (begin == -1)
		{
			begin = dc.indexOf(prefix);
			if (begin != 0)
			return null;
		}
		else
		{
			begin += 2;
		}
		var end = document.cookie.indexOf(";", begin);
		if (end == -1)
		{
			end = dc.length;
		}
		return unescape(dc.substring(begin + prefix.length, end));
	}

	// 
	// saves toggle state
	//
	oySyghtHighlight.prototype.saveStateCookie = function (){
		if (this.cookieName){
			var memento = "on";
			if (!this.on){
				memento = "off";
			}
			this.setCookie(this.cookieName + "_STATE", escape(memento));
		}
	}


	//
	// loads cookie state
	//
	oySyghtHighlight.prototype.loadStateCookie = function (){
		if (this.cookieName){
			var memento = unescape(this.getCookie(this.cookieName + "_STATE"));
			if (memento == "on") {
				this.on = true;
			}
			if (memento == "off") {
				this.on = false;
			}
		} 
	}



	//
	// save terms to cookie, so terms are persistent between page calls
	//
	oySyghtHighlight.prototype.saveTermsCookie = function (terms){
		var buf = "";

		if (terms.length != 0){
			for (var i=0; i < terms.length; i++){
				if (i != 0){
					buf += "&";
				}
				buf += escape(terms[i]);
			}
		}
		
		if (this.cookieName){
			this.setCookie(this.cookieName, escape(buf));
		}
	}

	//
	// load terms from cookie, so terms are persistent between page calls
	//
	oySyghtHighlight.prototype.loadTermsCookie = function(){
		var terms = null;
		if (this.cookieName){
			terms = unescape(this.getCookie(this.cookieName));
		}

		if (terms != null && terms != ""){
			return terms.split("&");
		} else {
			return new Array();
		}
	}


	//
	// load terms from the document location url or the document referrer url
	//
	oySyghtHighlight.prototype.loadTermsQryString = function (qry) {
		var terms = new Array();

		if (!document.createElement) return terms;

		// force cast to string if weird things like window.location, which are actually objects
		var ref = "" + qry;
		if (ref == null || ref.indexOf('?') == -1) return terms;

		qs = ref.substr(ref.indexOf('?')+1);
		qsa = qs.split('&');
		for (i=0;i<qsa.length;i++) {
			qsip = qsa[i].split('=');
				if (qsip.length == 1) continue;
				if (qsip[0] == 'q' || qsip[0] == 'p') { // q= for Google, p= for Yahoo
					words = unescape(qsip[1].replace(/\+/g,' ')).split(/\s+/);			
					for (w=0; w<words.length; w++) {
						var word = words[w];

						// remove non-text chars
						word = word.replace(/\+/g, "");
						word = word.replace(/\-/g, "");
						word = word.replace(/\~/g, "");						
						word = word.replace(/\:/g, "");
						word = word.replace(/\,/g, "");
						word = word.replace(/\./g, "");

						// trim white space
						word = word.replace(/^\s+|\s+$/g, '');

						terms.push(word);
					}
				}
		}

		return terms;
	}



	//
	// updates highlight terms and performs syntax highlight
	//
	oySyghtHighlight.prototype.applyTerms = function(terms){
		this.hiNodes = 0;	// count of decorated nodes to avoid highlight of each letter
		for (var i=0; i < terms.length; i++){
			this.highlightWord(document.getElementsByTagName("body")[0], terms[i]);
		}
	}

	//
	// process DOM and highlight one term
	//
	oySyghtHighlight.prototype.highlightWord = function (node, word) {
		if (this.hiNodes > 100){
			return;
		}

		// Iterate into this nodes childNodes
		if (node.hasChildNodes) {
			var hi_cn;
			for (hi_cn=0;hi_cn<node.childNodes.length;hi_cn++) {
				this.highlightWord(node.childNodes[hi_cn],word);
			}
		}

		// choose the class name to apply
		var targetClass = this.classNameOn;
		if (!this.on){
			targetClass = this.classNameOff;
		}
		
		// And do this node itself
		if (node.nodeType == 3) { // text node
			tempNodeVal = node.nodeValue.toLowerCase();
			tempWordVal = word.toLowerCase();
			if (tempNodeVal.indexOf(tempWordVal) != -1) {
				pn = node.parentNode;

				// 9 = top of doc
				checkn = pn;
				while (
					checkn.nodeType != 9 && 
					checkn.nodeName.toLowerCase() != 'body'
				) { 				
					// check if we're inside a "nosearchhi" zone
					if (checkn.className.match(/\bnosearchhi\b/)) { 
						return; 
					}
					checkn = checkn.parentNode;
				}

				// word has not already been highlighted!
				if (pn.className != targetClass) {
					nv = node.nodeValue;
					ni = tempNodeVal.indexOf(tempWordVal);
				
					// Create a load of replacement nodes
					before = document.createTextNode(nv.substr(0,ni));
					docWordVal = nv.substr(ni,word.length);
					after = document.createTextNode(nv.substr(ni+word.length));
					hiwordtext = document.createTextNode(docWordVal);
					hiword = document.createElement("span");
					hiword.className = targetClass;
					hiword.appendChild(hiwordtext);
					pn.insertBefore(before,node);
					pn.insertBefore(hiword,node);
					pn.insertBefore(after,node);
					pn.removeChild(node);

					this.active = true;
					this.hiNodes++;
				}
			}
		}
	}