/*
		garble.js

Written 10 August 2004 by Paul Novitski, novitskisoftware.com
-----------------------------
Last revised:

11 Aug 2004 pdn
- Allow the webmaster to decide whether to garble the same page element that's been moused over, or to garble another random element on the page (with the "garble" class).   See bGarbleRandom below.
- Limit the garble loop to N iterations to try to stop it crashing IE5/Mac.
18 Aug 2004 pdn
- Exclude Mac IE 5.x because it gets carried away when it runs the script.

-----------------------------
This script replaces moused-over text with random characters, then restores the original text on mouseout.

Logic:

- initialize:
	- create invisible "GarbleBin" div if it doesn't already exist.
	- assign mouseover & mouseout behaviors to all page elements with the class "garble".

- onmouseover:
	- select (randomly if bGarbleRandom is true) a page element to garble.
	- get innerHTML.
	- store original text in GarbleBin.
	- transform all non-tag text.

- onmouseout:
	- restore innerHTML from GarbleBin.

*/

//=========================
// global definitions
//=========================

//-------------------------
// Webmaster, you can modify these values to change program behavior & to suit your markup:
//-------------------------
// Random switch:	
//		if false, the moused-over element is garbled.
//		if true, a random element is garbled.
var bGarbleRandom = true;

// limit to the garble loop to prevent IE5/Mac from going loco
var GarbleLoopLimit = 50;

// class name of elements wanting the garble behavior
var sGarbleClass = "garble";

// the lowest character value to be generated
var iGarbleFloor = 100;

// the spread of generated character values
var GarbleRange = 400;


//-------------------------
// Please do not modify these other global variables:
//-------------------------
// name of the div that temporarily holds ungarbled content.
var sGarbleBin = "GarbleBin";

// pointer to the object that holds original text so we don't have to search for it
var oGarbleBin;

// store the currently garbled element
var oGarbleSubject = null;

// array of garble elements on the page
var aGarbles;


//=========================
function GarbleInit()
//=========================
/* This function initializes the garble functionality on the page.
	1) Create the GarbleBin if it doesn't already exist.
	2) Set the mouseover & mouseout events for all marked page elements.
	3) Collect an array of elements with the garble class.

This function must called when the page has completely loaded, 
either in JavaScript:
		window.onload = GarbleInit;
or in HTML:
		<body onload="GarbleInit()">
*/
//=========================
{
	//jsAlert("function GarbleInit()");
	
		// bail if the browser doesn't understand this script
		if (!document.getElementById) return //jsAlert("!document.getElementById");
		//if (!document.style) return;
	
	// bail if this is Mac IE5
	jsBrowserDetect();
	//jsAlert(OS + " " + browser + " " + browserversion);
		if (browser == "Internet Explorer" && OS == "Mac" && browserversion.indexOf("5") == 0) return;
	

//	1) Create the GarbleBin if it doesn't already exist.

	// does the bin already exist?
	oGarbleBin = document.getElementById(sGarbleBin);
		// if not:
		if (!oGarbleBin)
		{
			// create the bin
			oGarbleBin = document.createElement("div");
			oGarbleBin.id = sGarbleBin;
			document.body.appendChild(oGarbleBin);
		}
	// make sure it's invisible
	oGarbleBin.style.display = "none";

//	2) Set the mouseover & mouseout events for all marked page elements.
//	3) Collect an array of elements with the garble class.

	// initialize the global array of garble elements
	aGarbles = new Array();
	
	// set up a Regular Expression to detect the garble class as a discrete word
	// (must begin the class name or be preceded by a space, 
	//  AND must end the class name or be followed by a space)
	var rRegexp = new RegExp("(^| |\s)" + sGarbleClass + "(\s| |$)");

	// generate array of all elements on the page
	var aAll = document.getElementsByTagName("*");
	
	// loop through them:
	for (var iItem = 0; iItem < aAll.length; iItem++)
	{
		// get the current element's class
		var sClass = aAll[iItem].className;

//jsAlert("Element " + iItem + "\ntagName " + aAll[iItem].tagName + "\nclassName " + aAll[iItem].className);
		
			// got a class?
			if (sClass)
			{
				// if the element's class name contains the garble class as a discrete word...
				if (sClass.match(rRegexp))
				{
					// assign behaviors
					//jsAlert("Assigning garble behavior to " + aAll[iItem].tagName);
					aAll[iItem].onmouseover = GarbleOn;
					aAll[iItem].onmouseout = GarbleOff;
					
					// append this object to the array of elements with the garble class
					aGarbles[aGarbles.length] = aAll[iItem];

				} // if (sClass.match(rRegexp))
			} // if (sClass)
	} // for
} // function


//=========================
function GarbleOn()
//=========================
/*	This function:
	- randomly selects a page element with the "garble" class, 
	- saves its original content to the garble bin, and 
	- replaces its text with garble (preserving nested HTML tags).
*/
{
		// stop event bubbling
//		if (window.event.cancelBubble) window.event.cancelBubble = true;

	//jsAlert("function GarbleOn()");
	
		// don't do anything if we can't restore the original text
		if (!oGarbleBin) return;
	
		// don't garble an element if another element is still in process
		if (oGarbleSubject) return;
		if (oGarbleBin.innerHTML != "") return;
	
	// select a garble element & remember it for GarbleOff()
	oGarbleSubject = GarbleSelect(this);

		// if no object was selected, do nothing
		if (!oGarbleSubject) return;
	
	// save original text
	oGarbleBin.innerHTML = oGarbleSubject.innerHTML;
	
	// garble the text
	var sText = oGarbleSubject.innerHTML;
	var sResult = "";
	var iPtr = 0;
	var bDoingTag = false;
	var sEOL = "<";
	
	// find all instances of text between inner tags
	for (var iLoop=0; iLoop < GarbleLoopLimit; iLoop++)
	{
		//jsAlert("BEFORE:\n\nsText:\n" + sText + "\n\nsResult:\n" + sResult);

			// don't garble nested HTML
			if (sText.substr(0,1) == "<")
			{
				bDoingTag = true;
				sEOL = ">";
			} else {
				bDoingTag = false;
				sEOL = "<";
			}

		// find the length of the current segment (till sEOL or end of string)
		var iLen = sText.indexOf(sEOL);
			if (iLen < 0) iLen = sText.length;
			
			// all done?
			if (iLen < 0)
			{
				// gather the rest of the string and exit loop
				sResult += sText;
				break;
			}
			
			// extract the current segment & garble it if not an HTML tag
			if (bDoingTag)
			{
				// don't garble HTML tags
				iLen = iLen + 1;
				sResult += sText.substr(0, iLen);
			} else {
				// garble the current segment
				sResult += GarbleStr(sText.substr(0, iLen));
			}
		//jsAlert("Current chunk:\n" + sText.substr(0, iLen));

		// remove current segment from string
		sText = sText.substr(iLen);
		
		//jsAlert("AFTER:\n\nsText:\n" + sText + "\n\nsResult:\n" + sResult);

			// all done?
			if (sText == "") break;
	} // for loop

	// replace the text with garbled text	
	//jsAlert(sResult);
	oGarbleSubject.innerHTML = sResult;
}

//=========================
function GarbleSelect(argThis)
//=========================
/*
	Selects a page element with the "garble" class.
	Pass:	argThis = the object that has been moused over
*/
{
	// Default = use the same element the human has moused over
	var oSelected = argThis;

		// if the random switch is true, select one randomly
		if (bGarbleRandom)
		{
			// choose a garble element at random
			var iChosen = Math.floor(Math.random() * aGarbles.length);
			// Math.floor() returns an integer, rounded down
			// Math.random() returns a random value between 0 and 1
			// aGarbles.length is the number of page elements having the garble class

			// return that object	
			oSelected = aGarbles[iChosen];
		}

	return oSelected;
}


//=========================
function GarbleStr(argText)
//=========================
{
//alert("function GarbleStr('" + argText + "')");
	var aResult = new Array();

	// process each character of the string
	for (var iPtr = 0; iPtr < argText.length; iPtr++)
	{
		// get the current character
		var sChar = argText.charAt(iPtr);
		
			// got white space?
			if (sChar.match(/[ \s]/))
			//if (sChar == " ")
			{
				// preserve spaces
				aResult[iPtr] = sChar;
			} else {
				// garble everything else
				var iChar = Math.floor(Math.random() * GarbleRange) + iGarbleFloor;	 
				aResult[iPtr] = "&#" + iChar + ";";
			}
	}
	// return the garbled string
	return aResult.join("");
}

//=========================
function GarbleOff()
//=========================
{
		// stop event bubbling
		//if (window.event.cancelBubble) window.event.cancelBubble = true;

		// don't do anything if we can't restore the original text
		if (!oGarbleBin) return;
		
		// don't do anything if we haven't just garbled an object
		if (oGarbleBin.innerHTML == "") return;
		if (!oGarbleSubject) return;
	
	// restore the original text
	oGarbleSubject.innerHTML = oGarbleBin.innerHTML;
	
	// reset globals so we can garble something else
	oGarbleBin.innerHTML = "";
	oGarbleSubject = null;
}

//=========================
function jsAlert(argText)
//=========================
{
	alert(argText);
}



/*=========================
	browser-detect
	script by Peter-Paul Koch
	http://www.quirksmode.org/js/detect.html
	variable names modified by Paul Novitski 18-Aug-2004
=========================*/

var browserdetect, OS, browser, browserversion, browserstring;

function jsBrowserDetect()
{

	browserdetect = navigator.userAgent.toLowerCase();

		if (jsBrowserCheckIt('konqueror'))
		{
			browser = "Konqueror";
			OS = "Linux";
		}
		else if (jsBrowserCheckIt('safari')) browser = "Safari"
		else if (jsBrowserCheckIt('omniweb')) browser = "OmniWeb"
		else if (jsBrowserCheckIt('opera')) browser = "Opera"
		else if (jsBrowserCheckIt('webtv')) browser = "WebTV";
		else if (jsBrowserCheckIt('icab')) browser = "iCab"
		else if (jsBrowserCheckIt('msie')) browser = "Internet Explorer"
		else if (!jsBrowserCheckIt('compatible'))
		{
			browser = "Netscape Navigator"
			browserversion = browserdetect.charAt(8);
		}
		else browser = "An unknown browser";

		if (!browserversion) browserversion = browserdetect.charAt(place + browserstring.length);

		if (!OS)
		{
			if (jsBrowserCheckIt('linux')) OS = "Linux";
			else if (jsBrowserCheckIt('x11')) OS = "Unix";
			else if (jsBrowserCheckIt('mac')) OS = "Mac"
			else if (jsBrowserCheckIt('win')) OS = "Windows"
			else OS = "an unknown operating system";
		}
}

function jsBrowserCheckIt(string)
{
	place = browserdetect.indexOf(string) + 1;
	browserstring = string;
	return place;
}


//=========================
// initialize
//=========================
// run the init function when the page loads
window.onload = GarbleInit;

