// $Header: /cvsroot/bitweaver/_bit_util/javascript/bitweaver.js,v 1.57 2010/03/14 15:02:38 wjames5 Exp $

// please modify this file and leave plenty of comments. This file will be
// compressed automatically. Please make sure you only use comments beginning
// with '//' and put comments on separate lines otherwise the packer will choke

// the beginning of the clean up of bitweaver core js - use name spaces
// if you are adding new features to this file add them to the BitBase object.
BitBase = {
	// constants
	// DATE - set in init()
	// newWindow used in closeWin()
	"newWindow":null,

	//-- initialization --//
	// desc:		setup called at the end of this file
	"init": function(){
		var self = BitBase;
		self.DATE = new Date();
		var tz_offset = -(self.DATE.getTimezoneOffset() * 60);
		self.DATE.setFullYear(self.DATE.getFullYear() + 1);
		self.setCookie("tz_offset", tz_offset);
		self.setCookie("javascript_enabled", "y");
	},

	"deprecatedFunc": function ( funcName ){
		alert( 'Warning: Use of deprecated global function ' + funcName + ' use name space BitBase: BitBase.' + funcName )
	},

	// Adds a function to be called at page load time
	"addLoadHook": function(func) {
		if ( typeof window.addEventListener != "undefined" ) {
			window.addEventListener( "load", func, false );
		} else if ( typeof window.attachEvent != "undefined" ) {
			window.attachEvent( "onload", func );
		} else {
			if ( window.onload != null ) {
				var oldOnload = window.onload;
				window.onload = function ( e ) {
					oldOnload( e );
						func(e);
				};
			} else {
				window.onload = func(e);
			}
		}
	},

	//-- cookie coercion --//
	// desc:		Creates a Cookie
	// params:		name = the name of the cookie
	//				value = the value placed in the cookie
	//				[expires] (optional) = the expiration date of the cookie (defaults to end of current session)
	//				[path] (optional) = the path for which the cookie is valid (defaults to path of calling document)
	//				[domain] (optional) = the domain for which the cookie is valid (defaults to domain of calling document)
	//				[secure] (optional) = Boolean value indicating if the cookie transmission requires a secure transmission
	// NOTE:	* an argument defaults when it is assigned null as a placeholder
	// 			* a null placeholder is not required for trailing omitted arguments
	"setCookie": function(name, value, expire, path, domain, secure) {
		var self = BitBase;
		var path = (path) ? path : bitCookiePath;
		var domain = escape((domain) ? domain : bitCookieDomain);
		var cookie_path = ((path) ? "; path=" + path : "");
		var cookie_domain = ((domain) ? "; domain=" + domain : "");
		var cookie_expire = ((expire)?expire:((self.DATE) ? "; expires=" + self.DATE.toGMTString() : ""));
		var cookie_secure =	((secure) ? "; secure" : "");
		var curCookie = name + "=" + escape(value)
				+ cookie_path
				+ cookie_domain
				+ cookie_expire
				+ cookie_secure;
		document.cookie = curCookie;
	},

	// desc:		creates a cookie if required and sets the element in the array
	// params: 		name - cookie name / key - in the cookie array / val - value to store
	"setCookieArray": function(name, key, val){
		var self = BitBase;
		var curval = self.getCookie(name);
		var newval;
		if (curval != null) {
			newval = self.unserialize(curval);
			newval[key] = val;
		}
		else {
			newval = new Array();
			newval[key] = val;
		}
		self.setCookie(name, BitBase.serialize(newval));
	},

	// desc:		Gets a Cookie and returns it's value
	// params:		name = the name of the desired cookie
	"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));
	},

	// desc:		Returns a value from a key stored in an array in a cookie.
	// params: 		name - cookie name / key - in the cookie array
	"getCookieArray": function(name, key) {
		var self = BitBase;
		var curval = self.getCookie(name);
		var val;
		if (curval != null) {
			var arr = self.unserialize(curval);
			val = arr[key];
		}
		return val;
	},

	// desc:		Deletes a Cookie
	// params:		name = the name of the cookie
	//				[path] (optional) = the path of the cookie (must be same path used when created)
	//				[domain] (optional) = the domain of the cookie (must be same domain used when created)
	// NOTE:		path and domain default if assigned null or omitted if no explicit argument proceeds
	"deleteCookie": function(name, path, domain) {
		var cookie_path = (path) ? path : bitCookiePath;
		var cookie_domain = escape((domain) ? domain : bitCookieDomain);
		if (getCookie(name)) {
			document.cookie = name + "="
				+ "; path=" +  cookie_path + "; domain=" + cookie_domain + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
		}
	},

	//-- DOM coercion --//
	// desc:		returns a dom element
	// parms:		id - element id to look up or if id is an element it will be returned
	"getElement": function(id){
		if(typeof(id) == "string"){
			if(document.layers){
				return document.layers[id];
			}else if(document.all){
				return document.all[id];
			}else if(document.getElementById){
				return document.getElementById(id);
			}
		}
		return id;
	},

	// desc:		sets display style
	// params:		elm a DOM element or id / val the style value to toggle / useCookie to persist the setting
	"setElementDisplay": function( elm, val, useCookie ){
		var self = BitBase;
		var obj = self.getElement( elm );
		if( obj != null ){ obj.style.display=val; }
		if (useCookie && obj.id) { self.setCookieArray('showhide', obj.id, (val=='none'?"c":"o")); }
	},

	// desc:		toggles an element's style between the value and none
	// params:		elm a DOM element or id / val the style value to toggle / useCookie to persist the setting
	"toggleElementDisplay": function( elm, val, useCookie ){
		var self = BitBase;
		var obj = self.getElement( elm );
		if( typeof(val) == 'undefined' ) {
			val = 'block';
		}
		var value = obj.style.display == 'none'?val:'none';
		self.setElementDisplay( obj, value, useCookie );
	},

	// desc:		convenience
	// params:		elm - ad DOM element or id / useCookie = any value (not 0 or null) to turn cookies on
	"showById": function( elm, useCookie ) {
		BitBase.setElementDisplay( elm, 'block', useCookie );
	},

	// desc:		convenience
	// params:		elm - ad DOM element or id / useCookie = any value (not 0 or null) to turn cookies on
	"hideById": function( elm, useCookie) {
		BitBase.setElementDisplay( elm, 'none', useCookie );
	},

	// shows or hides based on the showhide cookie
	"setupShowHide": function() {
		var self = BitBase;
		var curval = self.getCookie('showhide');
		if (curval != null) {
			var ids = self.unserialize(curval);
			for (id in ids) {
				if (ids[id] == "o") {
					self.showById(id);
				}
				else {
					self.hideById(id);
				}
			}
		}
	},

	// desc:		Toggles the visibility of dynamic variable passed to it
	// params:		name
	"toggleDynamicVar": function(name) {
		var self = BitBase;
		var elm1 = self.getElement('dyn_'+name+'_display');
		var elm2 = self.getElement('dyn_'+name+'_edit');
		if(elm1.style.display == "none") {
			elm2.style.display = "none";
			elm1.style.display = "inline";
		} else {
			elm1.style.display = "none";
			elm2.style.display = "inline";
		}
	},

	// desc:		convenience
	"showSpinner": function() {
		BitBase.setElementDisplay( 'spinner','block' );
	},

	// desc:		convenience
	"hideSpinner": function() {
		BitBase.setElementDisplay( 'spinner','none' );
	},

	// desc:		No Idea - used by insertAt
	"setSelectionRange": function(textarea, selectionStart, selectionEnd) {
		if (textarea.setSelectionRange) {
			textarea.focus();
			textarea.setSelectionRange(selectionStart, selectionEnd);
		} else if (textarea.createTextRange) {
			var range = textarea.createTextRange();
			textarea.collapse(true);
			textarea.moveEnd('character', selectionEnd);
			textarea.moveStart('character', selectionStart);
			textarea.select();
		}
	},

	// desc:		No Idea - used by insertAt
	"setCaretToPos": function(textarea, pos) {
		BitBase.setSelectionRange(textarea, pos, pos);
	},

	// desc:		inserts replaceString in elementId - used by QuickTags & Plugin Help
	// params:		elementId = a HTML Id / replaceString = string
	"insertAt": function(elementId, replaceString) {
		var self = BitBase;
		//inserts given text at selection or cursor position
		//substrings in replaceString to be replaced by the selection if a selection was done
		var toBeReplaced = /text|page|textarea_id/;

		// FCKEditor is completely different
		if (document.FCKEditorLoaded) {
			oEditor = FCKeditorAPI.GetInstance(elementId);
			// Check if it is a fckeditor. If not fall back on old code.
			if (oEditor) {
				// Fetching selection can't be done through the 'Selection'. Stupid.
				if (document.all) {
					oSel = oEditor.EditorDocument.selection.createRange().text;
				} else {
					oSel = oEditor.EditorWindow.getSelection();
				}
				// Convert oSel to a string.
				oSel = "" + oSel;
				if (oSel.length > 0) {
					replaceString = replaceString.replace(toBeReplaced, oSel);
					// Delete selection
					oEditor.Selection.Delete();
				}
				oEditor.InsertHtml(replaceString);
				return;
			}
		}

		var textarea = document.getElementById(elementId);

		if (textarea.setSelectionRange) {
			//Mozilla UserAgent Gecko-1.4
			var selectionStart = textarea.selectionStart;
			var selectionEnd = textarea.selectionEnd;
			 // has there been a selection
			if (selectionStart != selectionEnd) {
				var newString = replaceString.replace(toBeReplaced, textarea.value.substring(selectionStart, selectionEnd));
				textarea.value = textarea.value.substring(0, selectionStart)
					+ newString
					+ textarea.value.substring(selectionEnd);
				self.setSelectionRange(textarea, selectionStart, selectionStart + newString.length);
			// set caret
			} else {
				textarea.value = textarea.value.substring(0, selectionStart)
					+ replaceString
					+ textarea.value.substring(selectionEnd);
				self.setCaretToPos(textarea, selectionStart + replaceString.length);
			}
		} else if (document.selection) {
			//UserAgent IE-6.0
			textarea.focus();
			var range = document.selection.createRange();
			if (range.parentElement() == textarea) {
				var isCollapsed = range.text == '';
				if (! isCollapsed)	{
					range.text = replaceString.replace(toBeReplaced, range.text);
					range.moveStart('character', -range.text.length);
					range.select();
				} else {
					range.text = replaceString;
			}	}
		} else {
			//UserAgent Gecko-1.0.1 (NN7.0)
			var obj = self.getElement(elementId);
			obj.getElement(elementId).value = obj.value + replaceString;
			//alert("don't know yet how to handle insert" + document);
		}
	},

	// function:	flipMulti
	// desc:		Toggles visibility for multiple HTML elements - can be used with an HTML Selector
	//				On the first call - Shows the window id's defined by (header) & (numbr) & saves them in a variable
	//				On subsequent calls - Hide the saved windows and shows the new windows
	// added by: 	StarRider
	// date:		1/5/06
	// Note			For Cross-Browser compatibility - An HTML elements id's must begin with a Letter or String.
	//				Operates by appending a string (header) with the number (numbr) to show/hide the window id.
	//				With multiple windows - the id's need to be sequencial eg. each id is 1 greater than the last
	//				with the first being (numbr).
	// example:		When used in a tpl file with div elements: $header='dog';
	//				<div id="{$header}1" style="display:none;">This Data</div> / the id is "dog1"
	//				<div id="{$header}2" style="display:none;">That Data</div> / the id is "dog2"
	//				The Selector: <select size="15" onchange="javascript:flipMulti(this.options[this.selectedIndex].value,2,2)">
	//				Each Option: <option value="{$header}">Whatever</option> / the value is "dog"
	//				To display the first set of divs - use:
	//				<script type="text/javascript"> flipMulti('{$header}',1,2); </script>
	// params:		elementIdStart = The string portion of the elements Id.
	//				elementIdNum = any number - that is appended to the elementIdStart forming the elements Id.
	//					If elementIdStart="dog" & elementIdNum=1 then the elements Id is "dog1"
	//				elements = a number (1-9/def=1) indicates how many elements to hide/display.
	//				zen = a number (1-3/def=1) allows multiple routines to use the function at the same time. Stores the
	//				text portion of the window Id (elementIdStart) in an array so that it can be hidden later.
	// Only the header portion of the id is saved
	"flipArr":[0,0,0],
	"flipMulti": function(elementIdStart,elementIdNum,elements,zen){
		var self = BitBase;
		if(elementIdStart && elementIdNum) {
			if(arguments.length<1) elementIdNum=1;
			// elementIdNum has to be a number
			elementIdNum=(elementIdNum*10)/10;
			if(arguments.length<2) elements=1;
			// elements has to be a number
			elements=(elements*10)/10;
			if(elements<1 || elements>9) elements=1;
			if(arguments.length<3) zen=1;
			// zen has to be a number
			zen=(zen*10)/10;
			if(!zen || zen<1 || zen>3) zen=1;
			var i=0;
			do {
				if(self.flipArr[zen-1]!=0) self.hideById(self.flipArr[zen-1]+(elementIdNum+i));
				self.showById(elementIdStart+(elementIdNum+i));
			} while (++i <= elements-1);
			self.flipArr[zen-1]=elementIdStart;
		}
	},

	// desc:		Toggles a HTML elements visibility with an Icon. Use Cookies to make it stay that way.
	// params:		elementId is an HTML Id for the window to be displayed/hidden
	"flipIcon": function(elementId) {
		var self = BitBase;
		var pic = new Image();
		if (elementId && document.getElementById(elementId).style && document.getElementById(elementId).style.display && document.getElementById(elementId).style.display == "none") {
			pic.src = bitIconDir + "/expanded.gif";
			self.showById(elementId,1);
		} else {
			pic.src = bitIconDir + "/collapsed.gif";
			self.hideById(elementId,1);
		}
		document.getElementById(elementId+"img").src = pic.src;
	},

	// desc:		Toggles the state of a flipped List after page reload
	// params:		foo = a HTML Id of a List
	"setFlipIcon": function(elementId) {
		var self = BitBase;
		var pic = new Image();
		if (self.getCookie(elementId) == "o") {
			pic.src = bitIconDir + "/expanded.gif";
			self.showById(elementId);
		} else {
			pic.src = bitIconDir + "/collapsed.gif";
			self.hideById(elementId);
		}
		document.getElementById(elementId+"img").src = pic.src;
	},

	// desc:		Used to Expand/Collapse Lists
	// params:		elementId = a HTML Id
	"flipWithSign": function(elementId,cookie) {
		var self = BitBase;
		if( flipperEle = document.getElementById( "flipper"+elementId ) ) {
			if (document.getElementById(elementId).style.display == "none") {
				self.showById(elementId,cookie);
				flipperEle.firstChild.nodeValue = "[-]";
			} else {
				self.hideById(elementId,cookie);
				flipperEle.firstChild.nodeValue = "[+]";
			}
		}
	},

	// desc:		Toggles the state of a flipped List after page reload
	// params:		elementId = a HTML Id of a List
	"setFlipWithSign": function(elementId) {
		var self = BitBase;
		if( flipperEle = document.getElementById( "flipper"+elementId ) ) {
			if (self.getCookie(elementId) == "o") {
				self.showById(elementId);
				flipperEle.firstChild.nodeValue = "[-]";
			} else {
				self.hideById(elementId);
				flipperEle.firstChild.nodeValue = "[+]";
			}
		}
	},

	// desc:		Simple fade in/out of element based on http://www.switchonthecode.com/tutorials/javascript-tutorial-simple-fade-animation
	// params:		elementId = a HTML Id, fadeTime = millisecond to fade out
	"fade": function(elementId,fadeTime,direction) {
		var element = document.getElementById(elementId);
		if(element) {
			if( fadeTime==null ) {
				fadeTime = 500.0;
			}
			if(element.FadeState == null) {
				if(element.style.opacity == null || element.style.opacity == '' || element.style.opacity == '1') {
					element.FadeState = 2;
				} else {
					element.FadeState = -2;
				}
			}

			if(element.FadeState == 1 || element.FadeState == -1) {
				element.FadeState = element.FadeState == 1 ? -1 : 1;
				element.FadeTimeLeft = fadeTime - element.FadeTimeLeft;
			} else {
				element.FadeState = element.FadeState == 2 ? -1 : 1;
				element.FadeTimeLeft = fadeTime;
				setTimeout("BitBase.animateFade(" + new Date().getTime() + ",'" + elementId + "',"+fadeTime+")", 33);
			}  
		}
	},

	"animateFade": function(lastTick, elementId,fadeTime) {  
		var curTick = new Date().getTime();
		var elapsedTicks = curTick - lastTick;
		var element = document.getElementById(elementId);

		if(element) {
			if(element.FadeTimeLeft <= elapsedTicks) {
				if( element.FadeState == 1 ) {
					element.style.opacity = '1';
					element.style.filter = 'alpha(opacity = 100)';
					element.FadeState = 2;
				} else {
					element.style.opacity = '0';
					element.style.filter = 'alpha(opacity = 0)';
					element.FadeState = -2;
					element.style.display = 'none';
				}
				return;
			}

			element.FadeTimeLeft -= elapsedTicks;
			var newOpVal = element.FadeTimeLeft/fadeTime;
			if(element.FadeState == 1) {
				element.style.display = ''; // revert to default display
				newOpVal = 1 - newOpVal;
			}

			element.height = Math.round(newOpVal*element.offsetHeight)+"px";
			element.style.opacity = newOpVal;
			element.style.filter = 'alpha(opacity = ' + (newOpVal*100) + ')';

			setTimeout("BitBase.animateFade(" + curTick + ",'" + elementId + "',"+fadeTime+")", 33);
		}
	},

	// desc:		get the value of form elements: <input type=...>, <textarea ...> ... </textarea> and <select ...> ... </select>.
	// params:		elm - an element or id
	/*
	Script by RoBorg
	RoBorg@geniusbug.com
	http://javascript.geniusbug.com | http://www.roborg.co.uk
	Please do not remove or edit this message
	Please link to this website if you use this script!
	*/
	"getElementValue": function(elm){
		if(formElement = BitBase.getElement(elm)) {
			if(formElement.length != null) {
				var type = formElement[0].type;
			}
			if((typeof(type) == 'undefined') || (type == 0)) {
				var type = formElement.type;
			}

			switch(type)
			{
				case 'undefined': return;

				case 'radio':
					for(var x=0; x < formElement.length; x++) {
						if(formElement[x].checked == true) {
							return formElement[x].value;
						}
					}
					break;

				case 'select-multiple':
					var myArray = new Array();
					for(var x=0; x < formElement.length; x++)
						if(formElement[x].selected == true)
							myArray[myArray.length] = formElement[x].value;
					return myArray;

				case 'checkbox': return formElement.checked;

				default: return formElement.value;
			}
		}
	},

	// desc:		set the value of form elements: <input type=...>, <textarea ...> ... </textarea> and <select ...> ... </select>.
	// params:		elm - an element or id, value to set form to
	/*
	Script by RoBorg
	RoBorg@geniusbug.com
	http://javascript.geniusbug.com | http://www.roborg.co.uk
	Please do not remove or edit this message
	Please link to this website if you use this script!
	*/
	"setElementValue": function(elm, value){
		if(formElement = BitBase.getElement(elm)) {
			switch(formElement.type)
			{
				case 'undefined': return;
				case 'radio': formElement.checked = value; break;
				case 'checkbox': formElement.checked = value; break;
				case 'select-one': formElement.selectedIndex = value; break;

				case 'select-multiple':
					for(var x=0; x < formElement.length; x++)
						formElement[x].selected = value[x];
					break;

				default: formElement.value = value; break;
			}
		}
	},

	// function:	switchCheckboxes
	// desc:		Will Check / Uncheck all Checkboxes
	// params:		the_form = a HTML Id of a form
	//				elements_name = the name of the checkbox / see note
	//				switcher_name = UNKNOWN
	// NOTE:		checkboxes need to have the same name as elements_name
	// Example:	<input type="checkbox" name="my_ename[]">, will arrive as Array in php.
	"switchCheckboxes": function(the_form, elements_name, switcher_name) {
		var elements = document.getElementById(the_form).elements[elements_name];
		var elements_cnt = ( typeof (elements.length) != 'undefined') ? elements.length : 0;

		if (elements_cnt) {
			for (var i = 0; i < elements_cnt; i++) {
				elements[i].checked = document.forms[the_form].elements[switcher_name].checked;
			}
		} else {
			elements.checked = document.forms[the_form].elements[switcher_name].checked;
		}
		return true;
	},

	// desc:		disable form stuff after submission
	// params:		id = a HTML Id
	// note:		a button you disable with this function will not appear in $_REQUEST
	"disableSubmit": function(id) {
		if(document.getElementById) {
			// this is the way the standards work
			document.getElementById(id).disabled = true;
			document.getElementById(id).value = "Please Wait...";
		} else if(document.all) {
			// this is the way old msie versions work
			document.all[id].disabled = true;
		} else if(document.layers) {
			// this is the way nn4 works
			document.layers[id].disabled = true;
		}
	},

	// desc:		added for use in navigation dropdown
	// params:		elementId = a HTML Id
	// Example:	<select name="anything" onchange="go(this);">
	//			<option value="http://bitweaver.org">bitweaver.org</option>
	// 			</select>
	"go": function(o) {
		if (o.options[o.selectedIndex].value != "") {
			location = o.options[o.selectedIndex].value;
		}
		return false;
	},

	// desc:		Closes the window stored in newWindow - used by popUpWin
	// params:		None
	"closeWin": function(){
		var self = BitBase;
		if (self.newWindow != null)
			if(!self.newWindow.closed) self.newWindow.close();
	},

	// function:	popUpWin
	// desc:		span a new window which is XHTML 1.0 Strict compliant and in accordance with WCAG
	// params:		url:		the url for the new window
	//				type:		standard or fullscreen
	//				strWidth:	width of the window
	//				strHeight:	height of the spawned window
	// usage:		<a href="<URL>" title="{tr}Opens link in new window{/tr}" onkeypress="popUpWin(this.href,'standard',600,400);" onclick="popUpWin(this.href,'standard',600,400);return false;">{tr}FooBar{/tr}</a>
	"popUpWin": function(url, type, strWidth, strHeight) {
		var self = BitBase;
		self.closeWin();
		if (type == "fullScreen") {
			strWidth = screen.availWidth - 10;
			strHeight = screen.availHeight - 160;
		}
		var tools="";
		if (type == "standard" || type == "fullScreen") tools = "resizable,toolbar=no,location=no,scrollbars=yes,menubar=no,width="+strWidth+",height="+strHeight+",top=0,left=0";
		if (type == "console") tools = "resizable,toolbar=no,location=no,scrollbars=yes,width="+strWidth+",height="+strHeight+",left=0,top=0";
		self.newWindow = window.open(url, 'newWin', tools);
		self.newWindow.focus();
	},

	// swiped from this page: http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
	// utf.js - UTF-8 <=> UTF-16 convertion
	//
	// Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
	// Version: 1.0
	// LastModified: Dec 25 1999
	// This library is free.  You can redistribute it and/or modify it.

	// Interfaces:
	// utf8 = utf16to8(utf16);
	// utf16 = utf16to8(utf8);
	"utf16to8": function(str) {
		var out, i, len, c;

		out = "";
		len = str.length;
		for(i = 0; i < len; i++) {
			c = str.charCodeAt(i);
			if ((c >= 0x0001) && (c <= 0x007F)) {
				out += str.charAt(i);
			} else if (c > 0x07FF) {
				out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
				out += String.fromCharCode(0x80 | ((c >>  6) & 0x3F));
				out += String.fromCharCode(0x80 | ((c >>  0) & 0x3F));
			} else {
				out += String.fromCharCode(0xC0 | ((c >>  6) & 0x1F));
				out += String.fromCharCode(0x80 | ((c >>  0) & 0x3F));
			}
		}
		return out;
	},

	"utf8to16": function(str) {
		var out, i, len, c;
		var char2, char3;

		out = "";
		len = str.length;
		i = 0;
		while(i < len) {
			c = str.charCodeAt(i++);
			switch(c >> 4)
			{
			  case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
				// 0xxxxxxx
				out += str.charAt(i-1);
				break;
			  case 12: case 13:
				// 110x xxxx   10xx xxxx
				char2 = str.charCodeAt(i++);
				out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
				break;
			  case 14:
				// 1110 xxxx  10xx xxxx  10xx xxxx
				char2 = str.charCodeAt(i++);
				char3 = str.charCodeAt(i++);
				out += String.fromCharCode(((c & 0x0F) << 12) |
							   ((char2 & 0x3F) << 6) |
							   ((char3 & 0x3F) << 0));
				break;
			}
		}

		return out;
	},

	// swiped from this page: http://www.coolcode.cn/?p=171
	//
	// phpserializer.js - JavaScript to PHP serialize / unserialize class.
	//
	// This class is designed to convert php variables to javascript
	// and javascript variables to php with a php serialize unserialize
	// compatible way.
	//
	// Copyright (C) 2006 Ma Bingyao <andot@ujn.edu.cn>
	// Version: 3.0f
	// LastModified: Nov 30, 2006
	// This library is free.  You can redistribute it and/or modify it.
	// http://www.coolcode.cn/?p=171

	"serialize": function(o) {
		var self = BitBase;
		var p = 0, sb = [], ht = [], hv = 1;
		var classname = function(o) {
			if (typeof(o) == "undefined" || typeof(o.constructor) == "undefined") return '';
			var c = o.constructor.toString();
			c = self.utf16to8(c.substr(0, c.indexOf('(')).replace(/(^\s*function\s*)|(\s*$)/ig, ''));
			return ((c == '') ? 'Object' : c);
		};
		var is_int = function(n) {
			var s = n.toString(), l = s.length;
			if (l > 11) return false;
			for (var i = (s.charAt(0) == '-') ? 1 : 0; i < l; i++) {
				switch (s.charAt(i)) {
					case '0':
					case '1':
					case '2':
					case '3':
					case '4':
					case '5':
					case '6':
					case '7':
					case '8':
					case '9': break;
					default : return false;
				}
			}
			return !(n < -2147483648 || n > 2147483647);
		};
		var in_ht = function(o) {
			for (k in ht) if (ht[k] === o) return k;
			return false;
		};
		var ser_null = function() {
			sb[p++] = 'N;';
		};
		var ser_boolean = function(b) {
			sb[p++] = (b ? 'b:1;' : 'b:0;');
		};
		var ser_integer = function(i) {
			sb[p++] = 'i:' + i + ';';
		};
		var ser_double = function(d) {
			if (isNaN(d)) d = 'NAN';
			else if (d == Number.POSITIVE_INFINITY) d = 'INF';
			else if (d == Number.NEGATIVE_INFINITY) d = '-INF';
			sb[p++] = 'd:' + d + ';';
		};
		var ser_string = function(s) {
			var utf8 = self.utf16to8(s);
			sb[p++] = 's:' + utf8.length + ':"';
			sb[p++] = utf8;
			sb[p++] = '";';
		};
		var ser_array = function(a) {
			sb[p++] = 'a:';
			var lp = p;
			sb[p++] = 0;
			sb[p++] = ':{';
			for (var k in a) {
				if (typeof(a[k]) != 'function') {
					is_int(k) ? ser_integer(k) : ser_string(k);
					__serialize(a[k]);
					sb[lp]++;
				}
			}
			sb[p++] = '}';
		};
		var ser_object = function(o) {
			var cn = classname(o);
			if (cn == '') ser_null();
			else if (typeof(o.serialize) != 'function') {
				sb[p++] = 'O:' + cn.length + ':"';
				sb[p++] = cn;
				sb[p++] = '":';
				var lp = p;
				sb[p++] = 0;
				sb[p++] = ':{';
				if (typeof(o.__sleep) == 'function') {
					var a = o.__sleep();
					for (var kk in a) {
						ser_string(a[kk]);
						__serialize(o[a[kk]]);
						sb[lp]++;
					}
				}
				else {
					for (var k in o) {
						if (typeof(o[k]) != 'function') {
							ser_string(k);
							__serialize(o[k]);
							sb[lp]++;
						}
					}
				}
				sb[p++] = '}';
			}
			else {
				 var cs = o.serialize();
				 sb[p++] = 'C:' + cn.length + ':"';
				 sb[p++] = cn;
				 sb[p++] = '":' + cs.length + ':{';
				 sb[p++] = cs;
				 sb[p++] = "}";
			 }
		 };
		 var ser_pointref = function(R) {
			 sb[p++] = "R:" + R + ";";
		 };
		 var ser_ref = function(r) {
			 sb[p++] = "r:" + r + ";";
		 };
		 var __serialize = function(o) {
			 if (o == null || o.constructor == Function) {
				 hv++;
				 ser_null();
			 }
			 else switch (o.constructor) {
				 case Boolean: {
					 hv++;
					 ser_boolean(o);
					 break;
				 }
				 case Number: {
					 hv++;
					 is_int(o) ? ser_integer(o) : ser_double(o);
					 break;
				 }
				 case String: {
					 hv++;
					 ser_string(o);
					 break;
				 }
	// @cc_on @
	// @if (@_jscript)
	//             case VBArray: {
	//                 o = o.toArray();
	//             }
	// @end @
				 case Array: {
					 var r = in_ht(o);
					 if (r) {
						 ser_pointref(r);
					 }
					 else {
						 ht[hv++] = o;
						 ser_array(o);
					 }
					 break;
				 }
				 default: {
					 var r = in_ht(o);
					 if (r) {
						 hv++;
						 ser_ref(r);
					 }
					 else {
						 ht[hv++] = o;
						 ser_object(o);
					 }
					 break;
				 }
			 }
		 };
		 __serialize(o);
		 return sb.join('');
	 },

	 "unserialize": function(ss) {
		 var self = BitBase;
		 var p = 0, ht = [], hv = 1; r = null;
		 var unser_null = function() {
			 p++;
			 return null;
		 };
		 var unser_boolean = function() {
			 p++;
			 var b = (ss.charAt(p++) == '1');
			 p++;
			 return b;
		 };
		 var unser_integer = function() {
			 p++;
			 var i = parseInt(ss.substring(p, p = ss.indexOf(';', p)));
			 p++;
			 return i;
		 };
		 var unser_double = function() {
			 p++;
			 var d = ss.substring(p, p = ss.indexOf(';', p));
			 switch (d) {
				 case 'NAN': d = NaN; break;
				 case 'INF': d = Number.POSITIVE_INFINITY; break;
				 case '-INF': d = Number.NEGATIVE_INFINITY; break;
				 default: d = parseFloat(d);
			 }
			 p++;
			 return d;
		 };
		 var unser_string = function() {
			 p++;
			 var l = parseInt(ss.substring(p, p = ss.indexOf(':', p)));
			 p += 2;
			 var s = self.utf8to16(ss.substring(p, p += l));
			 p += 2;
			 return s;
		 };
		 var unser_array = function() {
			 p++;
			 var n = parseInt(ss.substring(p, p = ss.indexOf(':', p)));
			 p += 2;
			 var a = [];
			 ht[hv++] = a;
			 for (var i = 0; i < n; i++) {
				 var k;
				 switch (ss.charAt(p++)) {
					 case 'i': k = unser_integer(); break;
					 case 's': k = unser_string(); break;
					 case 'U': k = unser_unicode_string(); break;
					 default: return false;
				 }
				 a[k] = __unserialize();
			 }
			 p++;
			 return a;
		 };
		 var unser_object = function() {
			 p++;
			 var l = parseInt(ss.substring(p, p = ss.indexOf(':', p)));
			 p += 2;
			 var cn = self.utf8to16(ss.substring(p, p += l));
			 p += 2;
			 var n = parseInt(ss.substring(p, p = ss.indexOf(':', p)));
			 p += 2;
			 if (eval(['typeof(', cn, ') == "undefined"'].join(''))) {
				 eval(['function ', cn, '(){}'].join(''));
			 }
			 var o = eval(['new ', cn, '()'].join(''));
			 ht[hv++] = o;
			 for (var i = 0; i < n; i++) {
				 var k;
				 switch (ss.charAt(p++)) {
					 case 's': k = unser_string(); break;
					 case 'U': k = unser_unicode_string(); break;
					 default: return false;
				 }
				 // the \0 breaks the javascript packer - it outputs \ and the 0 is missing.
				 // \1 works fine though. perhaps we can fix the packer or find a way not to use \0 here
				 //if (k.charAt(0) == '\0') {
				 //    k = k.substring(k.indexOf('\0', 1) + 1, k.length);
				 //}
				 o[k] = __unserialize();
			 }
			 p++;
			 if (typeof(o.__wakeup) == 'function') o.__wakeup();
			 return o;
		 };
		 var unser_custom_object = function() {
			 p++;
			 var l = parseInt(ss.substring(p, p = ss.indexOf(':', p)));
			 p += 2;
			 var cn = self.utf8to16(ss.substring(p, p += l));
			 p += 2;
			 var n = parseInt(ss.substring(p, p = ss.indexOf(':', p)));
			 p += 2;
			 if (eval(['typeof(', cn, ') == "undefined"'].join(''))) {
				 eval(['function ', cn, '(){}'].join(''));
			 }
			 var o = eval(['new ', cn, '()'].join(''));
			 ht[hv++] = o;
			 if (typeof(o.unserialize) != 'function') p += n;
			 else o.unserialize(ss.substring(p, p += n));
			 p++;
			 return o;
		 };
		 var unser_unicode_string = function() {
			 p++;
			 var l = parseInt(ss.substring(p, p = ss.indexOf(':', p)));
			 p += 2;
			 var sb = [];
			 for (var i = 0; i < l; i++) {
				 if ((sb[i] = ss.charAt(p++)) == '\\') {
					 sb[i] = String.fromCharCode(parseInt(ss.substring(p, p += 4), 16));
				 }
			 }
			 p += 2;
			 return sb.join('');
		 };
		 var unser_ref = function() {
			 p++;
			 var r = parseInt(ss.substring(p, p = ss.indexOf(';', p)));
			 p++;
			 return ht[r];
		 };
		 var __unserialize = function() {
			 switch (ss.charAt(p++)) {
				 case 'N': return ht[hv++] = unser_null();
				 case 'b': return ht[hv++] = unser_boolean();
				 case 'i': return ht[hv++] = unser_integer();
				 case 'd': return ht[hv++] = unser_double();
				 case 's': return ht[hv++] = unser_string();
				 case 'U': return ht[hv++] = unser_unicode_string();
				 case 'r': return ht[hv++] = unser_ref();
				 case 'a': return unser_array();
				 case 'O': return unser_object();
				 case 'C': return unser_custom_object();
				 case 'R': return unser_ref();
				 default: return false;
			 }
		 };
		 return __unserialize();
	},
	// end swipe

	// params:		w1 / w2 / w3
	"genPass": function(w1, w2, w3) {
		var vo = "aeiouAEU";
		var co = "bcdfgjklmnprstvwxzBCDFGHJKMNPQRSTVWXYZ0123456789_$%#";
		var s = Math.round(Math.random());
		var l = 8;
		var p = '';
		var has_num = false;
		for (i = 0; i < l; i++) {
			if (s) {
				letter = vo.charAt(Math.round(Math.random() * (vo.length - 1)));
				s = 0;
			} else {
				letter = co.charAt(Math.round(Math.random() * (co.length - 1)));
				s = 1;
			}
			
			if( ! isNaN( letter ) ) // Keep track of weather we put a number in the password
				has_num = true;
			
			p = p + letter;
		}
		
		if( ! has_num ) // If we never put a number in the password we'll put one in a random place now
			p = p.replace( p.charAt( Math.round(Math.random() * p.length - 1 ) ), Math.round(Math.random() * 9 ) );
		
		document.getElementById(w1).value = p;
		document.getElementById(w2).value = p;
		document.getElementById(w3).value = p;
	},

	/**
	 * XHConn - Simple XMLHTTP Interface - bfults@gmail.com - 2005-04-08        **
	 * Code licensed under Creative Commons Attribution-ShareAlike License      **
	 * http://creativecommons.org/licenses/by-sa/2.0/                           **
	 *
	 * var fnWhenDone = function ( pResponse ) {
	 *    	 alert( pResponse.responseText );
	 *     };
	 *     var ajax = new BitBase.SimpleAjax();
	 *     ajax.connect("mypage.php", "foo=bar&baz=qux", fnWhenDone, "POST");
	 * };
	 *
	 **/
	"SimpleAjax": function() {
		var xmlhttp, bComplete = false;
		try {
			xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
		}
		catch (e) { try { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); }
		catch (e) { try { xmlhttp = new XMLHttpRequest(); }
		catch (e) { xmlhttp = false; }}}
		if (!xmlhttp) return null;
		this.connect = function(sURL, sVars, fnDone, sMethod) {
			if (!xmlhttp) return false;
			bComplete = false;
			sMethod = sMethod || 'POST';
			sMethod = sMethod.toUpperCase();

			try {
				if (sMethod == "GET") {
					xmlhttp.open(sMethod, sURL+"?"+sVars, true);
					sVars = "";
				} else {
					xmlhttp.open(sMethod, sURL, true);
					xmlhttp.setRequestHeader("Method", "POST "+sURL+" HTTP/1.1");
					xmlhttp.setRequestHeader("Content-Type",
					"application/x-www-form-urlencoded");
				}
				xmlhttp.onreadystatechange = function() {
					if (xmlhttp.readyState == 4 && !bComplete) {
						bComplete = true;
						fnDone(xmlhttp);
					}
				};
					xmlhttp.setRequestHeader('X_REQUESTED_WITH', 'XMLHttpRequest');
				xmlhttp.send(sVars);
			}
			catch(z) { return false; }
			return true;
		};
		this.update = function( pUpdateEleId, sURL, sVars, sMethod ) {
			this.connect( sURL, sVars, function( pResponse ) { document.getElementById( pUpdateEleId ).innerHTML = pResponse.responseText; }, sMethod );
		};
		return this;
	},

	/**
	 *     Extremely lightweight fixIEDropMenu function to support css drop menus for all browsers without need for 30K of fixes/ie7.js
	 **/
	"fixIEDropMenu": function( pMenuId ) {
		if(document.getElementById(pMenuId)){
			var menuItems = document.getElementById(pMenuId).getElementsByTagName("LI");
			for( var i=0; i< menuItems.length; i++ ) {
				menuItems[i].onmouseover = function() {
					this.className += " iemenuhover";
				};
				menuItems[i].onmouseout = function() {
					this.className = this.className.replace(new RegExp(" iemenuhover\\b"), "");
				};
				/* In IE6 brute add iframes to anything that might have a z-index - someone else can narrow this better it doesn't need to be on all LIs */
				BitBase.bgIframe( menuItems[i] );
			}
		}
	},

	/**
	 * This mess right here ads an iframe behind any element you need to float over selects in that POS IE6
	 **/
	"bgIframe": function(e, s) {
		/* This is only for IE6 */
		if( document.all && (navigator.userAgent.toLowerCase().indexOf("msie 6.") != -1) ){
			/* @TODO enable override of params */
			s = {
				top : 'auto', /* auto == .currentStyle.borderTopWidth */
				left : 'auto', /* auto == .currentStyle.borderLeftWidth */
				width : 'auto', /* auto == offsetWidth */
				height : 'auto', /* auto == offsetHeight */
				opacity : true,
				src : 'javascript:void(0);'
			};

			var prop = function(n){return n&&n.constructor==Number?n+'px':n;},
			html = '<iframe class="bgiframe"frameborder="0"tabindex="-1"src="'+s.src+'"'+
			'style="display:block;position:absolute;z-index:-1;'+
			(s.opacity !== false?'filter:Alpha(Opacity=\'0\');':'')+
			'top:'+(s.top=='auto'?((parseInt(e.parentNode.style.borderTopWidth)||0)*-1)+'px':prop(s.top))+';'+
			'left:'+(s.left=='auto'?((parseInt(e.parentNode.style.borderLeftWidth)||0)*-1)+'px':prop(s.left))+';'+
			'width:'+(s.width=='auto'?e.parentNode.offsetWidth+'px':prop(s.width))+';'+
			'height:'+(s.height=='auto'?e.parentNode.offsetHeight+'px':prop(s.height))+';'+
			'"/></iframe>';
			e.innerHTML = html+e.innerHTML;
		}
	},

	/**
	 * This common short cut can raise all kinds of hell, since it behaves differently for different Ajax libraries
	 * in particularly, jQuery which does things like $('.someclass') to address multiple elements.
	 *
	 * It's use is strongly discouraged, particularly in distro'ed packages
	 **/

	"$": function() {
		var elements = new Array();
		for (var i = 0; i < arguments.length; i++) {
			var element = arguments[i];
			if (typeof element == 'string')
				element = document.getElementById(element);
			if (arguments.length == 1)
				return element;
			elements.push(element);
		}
		return elements;
	}


};

// init
BitBase.init();


//-- Slated for removal - as part of the clean up goal everything below is to eventually be removed --//

// NOTICE: this only appears to be used in quicktags - put it there
// function:	textareasize
// desc:		Modifies the dimensions of a textarea
// date:		Pre-bitweaver
// params:		elementId = a HTML Id to a textarea
//				height = nb pixels to add to the height (the number can be negative)
//				width = nb pixels to add to the width
//				formid = a HTML Id to form - see Note:
// Note:		* The form must have 2 input rows and cols
function textareasize(elementId, height) {
	textarea = document.getElementById( elementId );
	if (textarea && height != 0 && textarea.rows + height > 5) {
		textarea.rows += height;
		setCookie('rows', textarea.rows);
	}
}

// NOTICE: Slated for delete - no use in bitweaver known and redundant to getElementValue
function getRadioValue(elementName) {
	var element = document.getElementsByName(elementName);
	var bt_count = element.length; // can't use element.length in the loop, as it would decrement

	for( var i = 0; i < bt_count; i++ ) {
		if (element[i].checked == true) {
			return element[i].value;
		}
	}
}

// NOTICE: Slated for delete - no use in bitweaver known
// function:	setUserModuleFromCombo
// desc:		No Idea
// date:		Pre-bitweaver
// params:		id = a HTML Id
function setUserModuleFromCombo(id) {
	document.getElementById('usermoduledata').value = document.getElementById('usermoduledata').value
		+ document.getElementById(id).options[document.getElementById(id).selectedIndex].value;
}

// NOTICE: Slated for delete - no use in bitweaver known except this file
// function:	setSomeElement
// desc:		Adds a String to the value of elementId
// date:		Pre-bitweaver
// params:		elementId = a HTML Id
//				strng = the string to be added
function setSomeElement(elementId, strng) {
	document.getElementById(elementId).value = document.getElementById(elementId).value + strng;
}

// NOTICE: ALL the following are deprecated. See pass through call for replacement
function addLoadHook(func) {
	BitBase.deprecatedFunc( 'addLoadHook' );
	BitBase.addLoadHook(func);
}
function setCookieArray(cookie, key, value) {
	BitBase.deprecatedFunc( 'setCookieArray' );
	BitBase.setCookieArray(cookie, key, value);
}
function getCookieArray(cookie, key) {
	BitBase.deprecatedFunc( 'getCookieArray' );
	BitBase.getCookieArray(cookie, key);
}
function setCookie(name, value, expire, path, domain, secure) {
	BitBase.deprecatedFunc( 'setCookie' );
	BitBase.setCookie(name, value, expire, path, domain, secure);
}
function getCookie(name) {
	BitBase.deprecatedFunc( 'getCookie' );
	BitBase.getCookie(name);
}
function deleteCookie(name, path, domain) {
	BitBase.deprecatedFunc( 'deleteCookie' );
	BitBase.deleteCookie(name, path, domain);
}
function flip(elementId) {
	BitBase.deprecatedFunc( 'flip' );
	BitBase.toggleElementDisplay( elementId, 'block' );
}
function toggle(elementId) {
	BitBase.deprecatedFunc( 'toggle' );
	BitBase.toggleElementDisplay( elementId, 'block', 1 );
}
function setupShowHide() {
	BitBase.deprecatedFunc( 'setupShowHide' );
	BitBase.setupShowHide();
}
function utf16to8(str) {
	BitBase.deprecatedFunc( 'utf16to8' );
	BitBase.utf16to8(str);
}
function utf8to16(str) {
	BitBase.deprecatedFunc( 'utf8to16' );
	BitBase.utf8to16(str);
}
function serialize(o) {
	BitBase.deprecatedFunc( 'serialize' );
	BitBase.serialize(o);
}
function unserialize(ss) {
	BitBase.deprecatedFunc( 'unserialize' );
	BitBase.unserialize(ss);
}
function genPass(w1, w2, w3) {
	BitBase.deprecatedFunc( 'genPass' );
	BitBase.genPass(w1, w2, w3);
}
function toggle_dynamic_var(name) {
	BitBase.deprecatedFunc( 'toggle_dynamic_var' );
	BitBase.toggleDynamicVar( name );
}
function setSelectionRange(textarea, selectionStart, selectionEnd) {
	BitBase.deprecatedFunc( 'setSelectionRange' );
	BitBase.setSelectionRange(textarea, selectionStart, selectionEnd);
}
function setCaretToPos (textarea, pos) {
	BitBase.deprecatedFunc( 'setCaretToPos' );
	BitBase.setCaretToPos(textarea, pos);
}
function insertAt(elementId, replaceString) {
	BitBase.deprecatedFunc( 'insertAt' );
	BitBase.insertAt(elementId, replaceString);
}
function showById(elementId,useCookie) {
	BitBase.deprecatedFunc( 'showById' );
	BitBase.showById(elementId,useCookie);
}
function hideById(elementId,useCookie) {
	BitBase.deprecatedFunc( 'hideById' );
	BitBase.hideById(elementId,useCookie);
}
function flipMulti(elementIdStart,elementIdNum,elements,zen){
	BitBase.deprecatedFunc( 'flipMulti' );
	BitBase.flipMulti(elementIdStart,elementIdNum,elements,zen);
}
function flipIcon(elementId) {
	BitBase.deprecatedFunc( 'flipIcon' );
	BitBase.flipIcon(elementId);
}
function setFlipIcon(elementId) {
	BitBase.deprecatedFunc( 'setFlipIcon' );
	BitBase.setFlipIcon(elementId);
}
function flipWithSign(elementId,cookie) {
	BitBase.deprecatedFunc( 'flipWithSign' );
	BitBase.flipWithSign(elementId,cookie);
}
function setFlipWithSign(elementId) {
	BitBase.deprecatedFunc( 'setFlipWithSign' );
	BitBase.setFlipWithSign(elementId);
}
function getElementValue(formElementId){
	BitBase.deprecatedFunc( 'getElementValue' );
	BitBase.getElementValue(formElementId);
}
function setElementValue(formElementId, value){
	BitBase.deprecatedFunc( 'setElementValue' );
	BitBase.setElementValue(formElementId, value);
}
function switchCheckboxes(the_form, elements_name, switcher_name) {
	BitBase.deprecatedFunc( 'switchCheckboxes' );
	BitBase.switchCheckboxes(the_form, elements_name, switcher_name);
}
function disableSubmit(id) {
	BitBase.deprecatedFunc( 'disableSubmit' );
	BitBase.disableSubmit(id);
}
function go(o) {
	BitBase.deprecatedFunc( 'go' );
	BitBase.go(o);
}
function popUpWin(url, type, strWidth, strHeight) {
	BitBase.deprecatedFunc( 'popUpWin' );
	BitBase.popUpWin(url, type, strWidth, strHeight);
}
function closeWin(){
	BitBase.deprecatedFunc( 'closeWin' );
	BitBase.closeWin();
}

/**
 * SWFObject v1.5: Flash Player detection and embed - http://blog.deconcept.com/swfobject/
 *
 * SWFObject is (c) 2007 Geoff Stearns and is released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 */
if(typeof deconcept=="undefined"){var deconcept=new Object();}if(typeof deconcept.util=="undefined"){deconcept.util=new Object();}if(typeof deconcept.SWFObjectUtil=="undefined"){deconcept.SWFObjectUtil=new Object();}deconcept.SWFObject=function(_1,id,w,h,_5,c,_7,_8,_9,_a){if(!document.getElementById){return;}this.DETECT_KEY=_a?_a:"detectflash";this.skipDetect=deconcept.util.getRequestParameter(this.DETECT_KEY);this.params=new Object();this.variables=new Object();this.attributes=new Array();if(_1){this.setAttribute("swf",_1);}if(id){this.setAttribute("id",id);}if(w){this.setAttribute("width",w);}if(h){this.setAttribute("height",h);}if(_5){this.setAttribute("version",new deconcept.PlayerVersion(_5.toString().split(".")));}this.installedVer=deconcept.SWFObjectUtil.getPlayerVersion();if(!window.opera&&document.all&&this.installedVer.major>7){deconcept.SWFObject.doPrepUnload=true;}if(c){this.addParam("bgcolor",c);}var q=_7?_7:"high";this.addParam("quality",q);this.setAttribute("useExpressInstall",false);this.setAttribute("doExpressInstall",false);var _c=(_8)?_8:window.location;this.setAttribute("xiRedirectUrl",_c);this.setAttribute("redirectUrl","");if(_9){this.setAttribute("redirectUrl",_9);}};deconcept.SWFObject.prototype={useExpressInstall:function(_d){this.xiSWFPath=!_d?"expressinstall.swf":_d;this.setAttribute("useExpressInstall",true);},setAttribute:function(_e,_f){this.attributes[_e]=_f;},getAttribute:function(_10){return this.attributes[_10];},addParam:function(_11,_12){this.params[_11]=_12;},getParams:function(){return this.params;},addVariable:function(_13,_14){this.variables[_13]=_14;},getVariable:function(_15){return this.variables[_15];},getVariables:function(){return this.variables;},getVariablePairs:function(){var _16=new Array();var key;var _18=this.getVariables();for(key in _18){_16[_16.length]=key+"="+_18[key];}return _16;},getSWFHTML:function(){var _19="";if(navigator.plugins&&navigator.mimeTypes&&navigator.mimeTypes.length){if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","PlugIn");this.setAttribute("swf",this.xiSWFPath);}_19="<embed type=\"application/x-shockwave-flash\" src=\""+this.getAttribute("swf")+"\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\"";_19+=" id=\""+this.getAttribute("id")+"\" name=\""+this.getAttribute("id")+"\" ";var _1a=this.getParams();for(var key in _1a){_19+=[key]+"=\""+_1a[key]+"\" ";}var _1c=this.getVariablePairs().join("&");if(_1c.length>0){_19+="flashvars=\""+_1c+"\"";}_19+="/>";}else{if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","ActiveX");this.setAttribute("swf",this.xiSWFPath);}_19="<object id=\""+this.getAttribute("id")+"\" classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\">";_19+="<param name=\"movie\" value=\""+this.getAttribute("swf")+"\" />";var _1d=this.getParams();for(var key in _1d){_19+="<param name=\""+key+"\" value=\""+_1d[key]+"\" />";}var _1f=this.getVariablePairs().join("&");if(_1f.length>0){_19+="<param name=\"flashvars\" value=\""+_1f+"\" />";}_19+="</object>";}return _19;},write:function(_20){if(this.getAttribute("useExpressInstall")){var _21=new deconcept.PlayerVersion([6,0,65]);if(this.installedVer.versionIsValid(_21)&&!this.installedVer.versionIsValid(this.getAttribute("version"))){this.setAttribute("doExpressInstall",true);this.addVariable("MMredirectURL",escape(this.getAttribute("xiRedirectUrl")));document.title=document.title.slice(0,47)+" - Flash Player Installation";this.addVariable("MMdoctitle",document.title);}}if(this.skipDetect||this.getAttribute("doExpressInstall")||this.installedVer.versionIsValid(this.getAttribute("version"))){var n=(typeof _20=="string")?document.getElementById(_20):_20;n.innerHTML=this.getSWFHTML();return true;}else{if(this.getAttribute("redirectUrl")!=""){document.location.replace(this.getAttribute("redirectUrl"));}}return false;}};deconcept.SWFObjectUtil.getPlayerVersion=function(){var _23=new deconcept.PlayerVersion([0,0,0]);if(navigator.plugins&&navigator.mimeTypes.length){var x=navigator.plugins["Shockwave Flash"];if(x&&x.description){_23=new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/,"").replace(/(\s+r|\s+b[0-9]+)/,".").split("."));}}else{if(navigator.userAgent&&navigator.userAgent.indexOf("Windows CE")>=0){var axo=1;var _26=3;while(axo){try{_26++;axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+_26);_23=new deconcept.PlayerVersion([_26,0,0]);}catch(e){axo=null;}}}else{try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");_23=new deconcept.PlayerVersion([6,0,21]);axo.AllowScriptAccess="always";}catch(e){if(_23.major==6){return _23;}}try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(e){}}if(axo!=null){_23=new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));}}}return _23;};deconcept.PlayerVersion=function(_29){this.major=_29[0]!=null?parseInt(_29[0]):0;this.minor=_29[1]!=null?parseInt(_29[1]):0;this.rev=_29[2]!=null?parseInt(_29[2]):0;};deconcept.PlayerVersion.prototype.versionIsValid=function(fv){if(this.major<fv.major){return false;}if(this.major>fv.major){return true;}if(this.minor<fv.minor){return false;}if(this.minor>fv.minor){return true;}if(this.rev<fv.rev){return false;}return true;};deconcept.util={getRequestParameter:function(_2b){var q=document.location.search||document.location.hash;if(_2b==null){return q;}if(q){var _2d=q.substring(1).split("&");for(var i=0;i<_2d.length;i++){if(_2d[i].substring(0,_2d[i].indexOf("="))==_2b){return _2d[i].substring((_2d[i].indexOf("=")+1));}}}return "";}};deconcept.SWFObjectUtil.cleanupSWFs=function(){var _2f=document.getElementsByTagName("OBJECT");for(var i=_2f.length-1;i>=0;i--){_2f[i].style.display="none";for(var x in _2f[i]){if(typeof _2f[i][x]=="function"){_2f[i][x]=function(){};}}}};if(deconcept.SWFObject.doPrepUnload){if(!deconcept.unloadSet){deconcept.SWFObjectUtil.prepUnload=function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};window.attachEvent("onunload",deconcept.SWFObjectUtil.cleanupSWFs);};window.attachEvent("onbeforeunload",deconcept.SWFObjectUtil.prepUnload);deconcept.unloadSet=true;}}if(!document.getElementById&&document.all){document.getElementById=function(id){return document.all[id];};}var getQueryParamValue=deconcept.util.getRequestParameter;var FlashObject=deconcept.SWFObject;var SWFObject=deconcept.SWFObject;
// $Header: /cvsroot/bitweaver/_bit_util/javascript/libs/tabpane.js,v 1.21 2009/07/22 15:49:24 dansut Exp $

//-------------------------------------------------------------------------
//  						   Tab Pane 1.02
//-------------------------------------------------------------------------
//  					 Created by Erik Arvidsson
//  			  (http://webfx.eae.net/contact.html#erik)
//  				  For WebFX (http://webfx.eae.net/)
//-------------------------------------------------------------------------
//  			  Copyright (c) 1998 - 2003 Erik Arvidsson
//-------------------------------------------------------------------------

// please modify this file and leave plenty of comments. This file will be
// compressed automatically. Please make sure you only use comments beginning
// with '//' and put comments on separate lines otherwise the packer will choke

// Called to cause an FCKEditor to show. This fixes an incompatibility with
// tab pane and FCKEditor in Gecko browsers where the FCKEditor is created
// in a hidden tab. The editor fails to get focused without this.

// see http://webfx.eae.net/dhtml/tabpane/tabpane.html for details

function switchEditors(oNode) {
	var i=0;
	// We use this to avoid the error when this runs BEFORE
	// FKCEditor has created the API object.
	if (document.FCKEditorLoaded) {
		for (i=0;i<oNode.childNodes.length;i++) {
			childNode = oNode.childNodes.item(i);
			editor = FCKeditorAPI.GetInstance(childNode.name);
			if (editor && editor.EditorDocument && editor.EditMode == FCK_EDITMODE_WYSIWYG) {
				editor.SwitchEditMode();
				editor.SwitchEditMode();
			}
			switchEditors(childNode);
		}
	}
}

// This function is used to define if the browser supports the needed
// features
function hasSupport() {

	if (typeof hasSupport.support != "undefined") {
		return hasSupport.support;
	}

	var ie55 = /msie 5\.[56789]/i.test( navigator.userAgent );

	hasSupport.support = ( typeof document.implementation != "undefined" && document.implementation.hasFeature( "html", "1.0" ) || ie55 );

	// IE55 has a serious DOM1 bug... Patch it!
	if ( ie55 ) {
		document._getElementsByTagName = document.getElementsByTagName;
		document.getElementsByTagName = function ( sTagName ) {
			if ( sTagName == "*" ) {
				return document.all;
			} else {
				return document._getElementsByTagName( sTagName );
			}
		};
	}

	return hasSupport.support;
}


// The constructor for tab panes
// el : HTMLElement		The html element used to represent the tab pane
// bUseCookie : Boolean	Optional. Default is true. Used to determine whether to us
//						persistance using cookies or not

function WebFXTabPane( el, bUseCookie ) {
	if ( !hasSupport() || el == null ) { return; }

	this.element = el;
	this.element.tabPane = this;
	this.pages = [];
	this.selectedIndex = null;

	this.useCookie = bUseCookie != null ? bUseCookie : true;

	// <--- quick hack to set persistence only on pages where referrer == location
	var ref = document.referrer.split( /[#\?]/ );
	var loc = document.location.href.split( /[#\?]/ );
	if( loc[0] != ref[0] ) {
		this.useCookie = false;
	}
	// end quick hack - xing --->

	// add class name tag to class name
	this.element.className = this.classNameTag + " " + this.element.className;

	// add tab row
	this.tabRow = document.createElement( "div" );
	this.tabRow.className = "tabcontainer";
	el.insertBefore( this.tabRow, el.firstChild );

	// loop through child nodes and add them
	var cs = el.childNodes;
	var n;
	for (var i = 0; i < cs.length; i++) {
		if (cs[i].nodeType == 1 && ( cs[i].className == "tabpage" || cs[i].className.substr(0,8) == "tabpage ") ) {
			this.addTabPage( cs[i] );
		}
	}

	var tabIndex = 0;
	if ( this.useCookie ) {
		tabIndex = Number( WebFXTabPane.getCookie( "webfxtab_" + this.element.id ) );
		if ( isNaN( tabIndex ) || !this.pages[ tabIndex ] ) {
			tabIndex = 0;
		}
	}
	this.setSelectedIndex( tabIndex );
}

WebFXTabPane.prototype.classNameTag = "tabsystem";

WebFXTabPane.prototype.setSelectedIndex = function ( n ) {
	if (this.selectedIndex != n) {
		if (this.selectedIndex != null && this.pages[ this.selectedIndex ] != null ) {
			this.pages[ this.selectedIndex ].hide();
		}
		this.selectedIndex = n;
		this.pages[ this.selectedIndex ].show();

		if ( this.useCookie ) {
			WebFXTabPane.setCookie( "webfxtab_" + this.element.id, n, 1 );
		}
	}
};

WebFXTabPane.prototype.getSelectedIndex = function () {
	return this.selectedIndex;
};

WebFXTabPane.prototype.addTabPage = function ( oElement ) {
	if ( !hasSupport() ) { return; }

	if ( oElement.tabPage == this )	{
		return oElement.tabPage;
	}

	var n = this.pages.length;
	var tp = this.pages[n] = new WebFXTabPage( oElement, this, n );
	tp.tabPane = this;

	// move the tab out of the box
	this.tabRow.appendChild( tp.tab );

	if ( n == this.selectedIndex ) {
		tp.show();
	} else {
		tp.hide();
	}

	return tp;
};

WebFXTabPane.prototype.dispose = function () {
	this.element.tabPane = null;
	this.element = null;
	this.tabRow = null;

	for (var i = 0; i < this.pages.length; i++) {
		this.pages[i].dispose();
		this.pages[i] = null;
	}
	this.pages = null;
};



// Cookie handling
WebFXTabPane.setCookie = function ( sName, sValue, nDays ) {
	var expires = "";
	if ( nDays ) {
		var d = new Date();
		d.setTime( d.getTime() + nDays * 24 * 60 * 60 * 1000 );
		expires = "; expires=" + d.toGMTString();
	}

	document.cookie = sName + "=" + sValue + expires + "; path="+bitCookiePath;
};

WebFXTabPane.getCookie = function (sName) {
	var re = new RegExp( "(\;|^)[^;]*(" + sName + ")\=([^;]*)(;|$)" );
	var res = re.exec( document.cookie );
	return res != null ? res[3] : null;
};

WebFXTabPane.removeCookie = function ( name ) {
	setCookie( name, "", -1 );
};


// The constructor for tab pages. This one should not be used.
// Use WebFXTabPage.addTabPage instead
// el : HTMLElement			The html element used to represent the tab pane
// tabPane : WebFXTabPane	The parent tab pane
// nindex :	Number			The index of the page in the parent pane page array

function WebFXTabPage( el, tabPane, nIndex ) {
	if ( !hasSupport() || el == null ) { return; }

	this.element = el;
	this.element.tabPage = this;
	this.index = nIndex;

	var cs = el.childNodes;
	for (var i = 0; i < cs.length; i++) {
		if (cs[i].nodeType == 1 && ( cs[i].className == "tab" || cs[i].className.substr(0,4) == "tab ") ) {
			this.tab = cs[i];
			break;
		}
	}

	// insert a tag around content to support keyboard navigation


	var a = document.createElement( "A" );
	this.aElement = a;
	a.href = "#";
	a.onclick = function () { return false; };
	while ( this.tab.hasChildNodes() ) {
		a.appendChild( this.tab.firstChild );
	}
	this.tab.appendChild( a );


	// hook up events, using DOM0
	var oThis = this;
	// we can bind a custom onclick event to the tab by passing one in - this lets us associate additional events with the click of a tab -wjames5
	var oldOnclick = this.tab.onclick != null? this.tab.onclick:function(){};
	this.tab.onclick = function () { oThis.select(); oldOnclick(); };
	this.tab.onmouseover = function () { WebFXTabPage.tabOver( oThis ); };
	this.tab.onmouseout = function () { WebFXTabPage.tabOut( oThis ); };
}

WebFXTabPage.prototype.show = function () {
	var el = this.tab;
	var s = el.className + " tab-active";
	s = s.replace(/ +/g, " ");
	el.className = s;

	// Fix for FCKEditor focus bug
	switchEditors(this.element);
	this.element.style.display = "block";
};

WebFXTabPage.prototype.hide = function () {
	var el = this.tab;
	var s = el.className;
	// Packer doesn't like \- in regexp for some reason
	s = s.replace(/ tab.active/g, "");
	el.className = s;

	this.element.style.display = "none";
};

WebFXTabPage.prototype.select = function () {
	this.tabPane.setSelectedIndex( this.index );
};

WebFXTabPage.prototype.dispose = function () {
	// Safari only submits inputs which are not display:none
	// I suspect this is for "security" but that is stupid since we
	// can still hide it off screen like this.
	var safari = /^Apple/;
	// note the crazy 'unknown' check here. this is for IE (of course) whoes xmlhtml is not part of the js engine and willy nilly throws in crap outside the spec.
	if (this.element.style.display == "none" && typeof( navigator.vendor ) != "unknown" && navigator.vendor == safari) {
		this.element.style.position = "absolute";
		this.element.style.left = "-10000px";
		this.element.style.display = "block";
	}
	this.aElement.onclick = null;
	this.aElement = null;
	this.element.tabPage = null;
	this.tab.onclick = null;
	this.tab.onmouseover = null;
	this.tab.onmouseout = null;
	this.tab = null;
	this.tabPane = null;
	this.element = null;
};

WebFXTabPage.tabOver = function ( tabpage ) {
	var el = tabpage.tab;
	var s = el.className + " tab-hover";
	s = s.replace(/ +/g, " ");
	el.className = s;
};

WebFXTabPage.tabOut = function ( tabpage ) {
	var el = tabpage.tab;
	var s = el.className;
	// Packer doesn't like \- in regexp for some reason
	s = s.replace(/ tab.hover/g, "");
	el.className = s;
};


// This function initializes all uninitialized tab panes and tab pages
function setupAllTabs() {
	if ( !hasSupport() ) { return; }

	var all = document.getElementsByTagName( "*" );
	var l = all.length;
	var tabPaneRe = /tabpane/;
	var tabPageRe = /tabpage/;
	var cn, el;
	var parentTabPane;

	for ( var i = 0; i < l; i++ ) {
		el = all[i];
		cn = el.className;

		// no className
		if ( cn == "" ) { continue; }

		// uninitiated tab pane
		if ( tabPaneRe.test( cn ) && !el.tabPane ) {
			new WebFXTabPane( el );
		}

		// unitiated tab page wit a valid tab pane parent
		else if ( tabPageRe.test( cn ) && !el.tabPage && tabPaneRe.test( el.parentNode.className ) ) {
			el.parentNode.tabPane.addTabPage( el );
		}
	}
}

function disposeAllTabs() {
	if ( !hasSupport() ) { return; }

	var all = document.getElementsByTagName( "*" );
	var l = all.length;
	var tabPaneRe = /tabpane/;
	var cn, el;
	var tabPanes = [];

	for ( var i = 0; i < l; i++ ) {
		el = all[i];
		cn = el.className;

		// no className
		if ( cn == "" ) { continue; }

		// tab pane
		if ( tabPaneRe.test( cn ) && el.tabPane ) {
			tabPanes[tabPanes.length] = el.tabPane;
		}
	}

	for (var i = tabPanes.length - 1; i >= 0; i--) {
		tabPanes[i].dispose();
		tabPanes[i] = null;
	}
}


// initialization hook up

// DOM2
if ( typeof window.addEventListener != "undefined" ) {
	window.addEventListener( "load", setupAllTabs, false );
} else if ( typeof window.attachEvent != "undefined" ) {
	window.attachEvent( "onload", setupAllTabs );
	window.attachEvent( "onunload", disposeAllTabs );
} else {
	if ( window.onload != null ) {
		var oldOnload = window.onload;
		window.onload = function ( e ) {
			oldOnload( e );
			setupAllTabs();
		};
	} else {
		window.onload = setupAllTabs;
	}
}

// $Header: /cvsroot/bitweaver/_bit_util/javascript/libs/fat.js,v 1.6 2009/08/18 21:35:26 wjames5 Exp $

// please modify this file and leave plenty of comments. This file will be
// compressed automatically. Please make sure you only use comments beginning
// with '//' and put comments on separate lines otherwise the packer will choke

// @name      The Fade Anything Technique
// @namespace http://www.axentric.com/aside/fat/
// @version   1.0-RC1
// @author    Adam Michela

var Fat = {
	make_hex : function (r,g,b) {
		r = r.toString(16); if (r.length == 1) r = '0' + r;
		g = g.toString(16); if (g.length == 1) g = '0' + g;
		b = b.toString(16); if (b.length == 1) b = '0' + b;
		return "#" + r + g + b;
	},
	fade_all : function () {
		var a = document.getElementsByTagName("*");
		for (var i = 0; i < a.length; i++) 
		{
			var o = a[i];
			var r = /fade-?(\w{3,6})?/.exec(o.className);
			if (r)
			{
				if (!r[1]) r[1] = "";
				if (o.id) Fat.fade_element(o.id,null,null,"#"+r[1]);
			}
		}
	},
	fade_element : function (id, fps, duration, from, to) {
		if (!fps) fps = 30;
		if (!duration) duration = 3000;
		if (!from || from=="#") from = "#FFFF33";
		if (!to) to = this.get_bgcolor(id);

		var frames = Math.round(fps * (duration / 1000));
		var interval = duration / frames;
		var delay = interval;
		var frame = 0;

		if (from.length < 7) from += from.substr(1,3);
		if (to.length < 7) to += to.substr(1,3);

		var rf = parseInt(from.substr(1,2),16);
		var gf = parseInt(from.substr(3,2),16);
		var bf = parseInt(from.substr(5,2),16);
		var rt = parseInt(to.substr(1,2),16);
		var gt = parseInt(to.substr(3,2),16);
		var bt = parseInt(to.substr(5,2),16);

		var r,g,b,h;
		while (frame < frames) {
			r = Math.floor(rf * ((frames-frame)/frames) + rt * (frame/frames));
			g = Math.floor(gf * ((frames-frame)/frames) + gt * (frame/frames));
			b = Math.floor(bf * ((frames-frame)/frames) + bt * (frame/frames));
			h = this.make_hex(r,g,b);

			setTimeout("Fat.set_bgcolor('"+id+"','"+h+"')", delay);

			frame++;
			delay = interval * frame;
		}
		setTimeout("Fat.set_bgcolor('"+id+"','"+to+"')", delay);
	},
	set_bgcolor : function (id, c) {
		var o = document.getElementById(id);
		o.style.backgroundColor = c;
	},
	get_bgcolor : function (id) {
		var o = document.getElementById(id);
		while(o) {
			var c;
			if (window.getComputedStyle) c = window.getComputedStyle(o,null).getPropertyValue("background-color");
			if (o.currentStyle) c = o.currentStyle.backgroundColor;
			if ((c != "" && c != "transparent") || o.tagName == "BODY") { break; }
			o = o.parentNode;
		}
		if (c == undefined || c == "" || c == "transparent") c = "#FFFFFF";
		var rgb = c.match(/rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/);
		if (rgb) c = this.make_hex(parseInt(rgb[1]),parseInt(rgb[2]),parseInt(rgb[3]));
		return c;
	}
};

window.onload = function () {
	Fat.fade_all();
};

// FancyZoom.js - v1.1 - http://www.fancyzoom.com
//
// Copyright (c) 2008 Cabel Sasser / Panic Inc
// All rights reserved.
// 
//     Requires: FancyZoomHTML.js
// Instructions: Include JS files in page, call setupZoom() in onLoad. That's it!
//               Any <a href> links to images will be updated to zoom inline.
//               Add rel="nozoom" to your <a href> to disable zooming for an image.
// 
// Redistribution and use of this effect in source form, with or without modification,
// are permitted provided that the following conditions are met:
// 
// * USE OF SOURCE ON COMMERCIAL (FOR-PROFIT) WEBSITE REQUIRES ONE-TIME LICENSE FEE PER DOMAIN.
//   Reasonably priced! Visit www.fancyzoom.com for licensing instructions. Thanks!
//
// * Non-commercial (personal) website use is permitted without license/payment!
//
// * Redistribution of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
// * Redistribution of source code and derived works cannot be sold without specific
//   written prior permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


// {{{ BITMOD
// Modifications to make it work with bitweaver:
// rename setOpacity to _setOpacity to avoid conflicts with mochikit.
// }}} BITMOD


var includeCaption = true; // Turn on the "caption" feature, and write out the caption HTML
var zoomTime       = 3;    // Milliseconds between frames of zoom animation
var zoomSteps      = 10;   // Number of zoom animation frames
var includeFade    = 1;    // Set to 1 to fade the image in / out as it zooms
var minBorder      = 50;   // Amount of padding between large, scaled down images, and the window edges
var shadowSettings = '0px 5px 25px rgba(0, 0, 0)'; // Blur, radius, color of shadow for compatible browsers

var zoomImagesURI   = bitRootUrl+'util/javascript/fancyzoom/images-global/zoom/'; // Location of the zoom and shadow images

// Init. Do not add anything below this line, unless it's something awesome.

var myWidth = 0, myHeight = 0, myScroll = 0; myScrollWidth = 0; myScrollHeight = 0;
var zoomOpen = false, preloadFrame = 1, preloadActive = false, preloadTime = 0, imgPreload = new Image();
var preloadAnimTimer = 0;

var zoomActive = new Array(); var zoomTimer  = new Array(); 
var zoomOrigW  = new Array(); var zoomOrigH  = new Array();
var zoomOrigX  = new Array(); var zoomOrigY  = new Array();

var zoomID         = "ZoomBox";
var theID          = "ZoomImage";
var zoomCaption    = "ZoomCaption";
var zoomCaptionDiv = "ZoomCapDiv";

if (navigator.userAgent.indexOf("MSIE") != -1) {
	var browserIsIE = true;
}

// Zoom: Setup The Page! Called in your <body>'s onLoad handler.

function setupZoom() {
	prepZooms();
	insertZoomHTML();
	zoomdiv = document.getElementById(zoomID);  
	zoomimg = document.getElementById(theID);
}

// Zoom: Inject Javascript functions into hrefs pointing to images, one by one!
// Skip any href that contains a rel="nozoom" tag.
// This is done at page load time via an onLoad() handler.

function prepZooms() {
	if (! document.getElementsByTagName) {
		return;
	}
	var links = document.getElementsByTagName("a");
	for (i = 0; i < links.length; i++) {
		if (links[i].getAttribute("href")) {
			if (links[i].getAttribute("href").search(/(.*)\.(jpg|jpeg|gif|png|bmp|tif|tiff)/gi) != -1) {
				if (links[i].getAttribute("rel") != "nozoom") {
					links[i].onclick = function (event) { return zoomClick(this, event); };
					links[i].onmouseover = function () { zoomPreload(this); };
				}
			}
		}
	}
}

// Zoom: Load an image into an image object. When done loading, function sets preloadActive to false,
// so other bits know that they can proceed with the zoom.
// Preloaded image is stored in imgPreload and swapped out in the zoom function.

function zoomPreload(from) {

	var theimage = from.getAttribute("href");

	// Only preload if we have to, i.e. the image isn't this image already

	if (imgPreload.src.indexOf(from.getAttribute("href").substr(from.getAttribute("href").lastIndexOf("/"))) == -1) {
		preloadActive = true;
		imgPreload = new Image();

		// Set a function to fire when the preload is complete, setting flags along the way.

		imgPreload.onload = function() {
			preloadActive = false;
		};

		// Load it!
		imgPreload.src = theimage;
	}
}

// Zoom: Start the preloading animation cycle.

function preloadAnimStart() {
	preloadTime = new Date();
	document.getElementById("ZoomSpin").style.left = (myWidth / 2) + 'px';
	document.getElementById("ZoomSpin").style.top = ((myHeight / 2) + myScroll) + 'px';
	document.getElementById("ZoomSpin").style.visibility = "visible";	
	preloadFrame = 1;
	document.getElementById("SpinImage").src = zoomImagesURI+'zoom-spin-'+preloadFrame+'.png';  
	preloadAnimTimer = setInterval("preloadAnim()", 100);
}

// Zoom: Display and ANIMATE the jibber-jabber widget. Once preloadActive is false, bail and zoom it up!

function preloadAnim(from) {
	if (preloadActive != false) {
		document.getElementById("SpinImage").src = zoomImagesURI+'zoom-spin-'+preloadFrame+'.png';
		preloadFrame++;
		if (preloadFrame > 12) preloadFrame = 1;
	} else {
		document.getElementById("ZoomSpin").style.visibility = "hidden";    
		clearInterval(preloadAnimTimer);
		preloadAnimTimer = 0;
		zoomIn(preloadFrom);
	}
}

// ZOOM CLICK: We got a click! Should we do the zoom? Or wait for the preload to complete?
// todo?: Double check that imgPreload src = clicked src

function zoomClick(from, evt) {

	var shift = getShift(evt);

	// Check for Command / Alt key. If pressed, pass them through -- don't zoom!
	if (! evt && window.event && (window.event.metaKey || window.event.altKey)) {
		return true;
	} else if (evt && (evt.metaKey|| evt.altKey)) {
		return true;
	}

	// Get browser dimensions
	getSize();

	// If preloading still, wait, and display the spinner.
	if (preloadActive == true) {
		// But only display the spinner if it's not already being displayed!
		if (preloadAnimTimer == 0) {
			preloadFrom = from;
			preloadAnimStart();	
		}
	} else {
		// Otherwise, we're loaded: do the zoom!
		zoomIn(from, shift);
	}
	
	return false;
	
}

// Zoom: Move an element in to endH endW, using zoomHost as a starting point.
// "from" is an object reference to the href that spawned the zoom.

function zoomIn(from, shift) {

	zoomimg.src = from.getAttribute("href");

	// Determine the zoom settings from where we came from, the element in the <a>.
	// If there's no element in the <a>, or we can't get the width, make stuff up

	if (from.childNodes[0].width) {
		startW = from.childNodes[0].width;
		startH = from.childNodes[0].height;
		startPos = findElementPos(from.childNodes[0]);
	} else {
		startW = 50;
		startH = 12;
		startPos = findElementPos(from);
	}

	hostX = startPos[0];
	hostY = startPos[1];

	// Make up for a scrolled containing div.
	// TODO: This HAS to move into findElementPos.
	
	if (document.getElementById('scroller')) {
		hostX = hostX - document.getElementById('scroller').scrollLeft;
	}

	// Determine the target zoom settings from the preloaded image object

	endW = imgPreload.width;
	endH = imgPreload.height;

	// Start! But only if we're not zooming already!

	if (zoomActive[theID] != true) {

		// Clear everything out just in case something is already open

		if (document.getElementById("ShadowBox")) {
			document.getElementById("ShadowBox").style.visibility = "hidden";
		} else if (! browserIsIE) {
		
			// Wipe timer if shadow is fading in still
			if (fadeActive["ZoomImage"]) {
				clearInterval(fadeTimer["ZoomImage"]);
				fadeActive["ZoomImage"] = false;
				fadeTimer["ZoomImage"] = false;			
			}
			
			document.getElementById("ZoomImage").style.webkitBoxShadow = shadowSettings + '0.0)';			
		}
		
		document.getElementById("ZoomClose").style.visibility = "hidden";     

		// Setup the CAPTION, if existing. Hide it first, set the text.

		if (includeCaption) {
			document.getElementById(zoomCaptionDiv).style.visibility = "hidden";
			if (from.getAttribute('title') && includeCaption) {
				// Yes, there's a caption, set it up
				document.getElementById(zoomCaption).innerHTML = from.getAttribute('title');
			} else {
				document.getElementById(zoomCaption).innerHTML = "";
			}
		}

		// Store original position in an array for future zoomOut.

		zoomOrigW[theID] = startW;
		zoomOrigH[theID] = startH;
		zoomOrigX[theID] = hostX;
		zoomOrigY[theID] = hostY;

		// Now set the starting dimensions

		zoomimg.style.width = startW + 'px';
		zoomimg.style.height = startH + 'px';
		zoomdiv.style.left = hostX + 'px';
		zoomdiv.style.top = hostY + 'px';

		// Show the zooming image container, make it invisible

		if (includeFade == 1) {
			_setOpacity(0, zoomID);
		}
		zoomdiv.style.visibility = "visible";

		// If it's too big to fit in the window, shrink the width and height to fit (with ratio).

		sizeRatio = endW / endH;
		if (endW > myWidth - minBorder) {
			endW = myWidth - minBorder;
			endH = endW / sizeRatio;
		}
		if (endH > myHeight - minBorder) {
			endH = myHeight - minBorder;
			endW = endH * sizeRatio;
		}

		zoomChangeX = ((myWidth / 2) - (endW / 2) - hostX);
		zoomChangeY = (((myHeight / 2) - (endH / 2) - hostY) + myScroll);
		zoomChangeW = (endW - startW);
		zoomChangeH = (endH - startH);
		
		// Shift key?
	
		if (shift) {
			tempSteps = zoomSteps * 7;
		} else {
			tempSteps = zoomSteps;
		}

		// Setup Zoom

		zoomCurrent = 0;

		// Setup Fade with Zoom, If Requested

		if (includeFade == 1) {
			fadeCurrent = 0;
			fadeAmount = (0 - 100) / tempSteps;
		} else {
			fadeAmount = 0;
		}

		// Do It!
		
		zoomTimer[theID] = setInterval("zoomElement('"+zoomID+"', '"+theID+"', "+zoomCurrent+", "+startW+", "+zoomChangeW+", "+startH+", "+zoomChangeH+", "+hostX+", "+zoomChangeX+", "+hostY+", "+zoomChangeY+", "+tempSteps+", "+includeFade+", "+fadeAmount+", 'zoomDoneIn(zoomID)')", zoomTime);		
		zoomActive[theID] = true; 
	}
}

// Zoom it back out.

function zoomOut(from, evt) {

	// Get shift key status.
	// IE events don't seem to get passed through the function, so grab it from the window.

	if (getShift(evt)) {
		tempSteps = zoomSteps * 7;
	} else {
		tempSteps = zoomSteps;
	}	

	// Check to see if something is happening/open
  
	if (zoomActive[theID] != true) {

		// First, get rid of the shadow if necessary.

		if (document.getElementById("ShadowBox")) {
			document.getElementById("ShadowBox").style.visibility = "hidden";
		} else if (! browserIsIE) {
		
			// Wipe timer if shadow is fading in still
			if (fadeActive["ZoomImage"]) {
				clearInterval(fadeTimer["ZoomImage"]);
				fadeActive["ZoomImage"] = false;
				fadeTimer["ZoomImage"] = false;			
			}
			
			document.getElementById("ZoomImage").style.webkitBoxShadow = shadowSettings + '0.0)';			
		}

		// ..and the close box...

		document.getElementById("ZoomClose").style.visibility = "hidden";

		// ...and the caption if necessary!

		if (includeCaption && document.getElementById(zoomCaption).innerHTML != "") {
			// fadeElementSetup(zoomCaptionDiv, 100, 0, 5, 1);
			document.getElementById(zoomCaptionDiv).style.visibility = "hidden";
		}

		// Now, figure out where we came from, to get back there

		startX = parseInt(zoomdiv.style.left);
		startY = parseInt(zoomdiv.style.top);
		startW = zoomimg.width;
		startH = zoomimg.height;
		zoomChangeX = zoomOrigX[theID] - startX;
		zoomChangeY = zoomOrigY[theID] - startY;
		zoomChangeW = zoomOrigW[theID] - startW;
		zoomChangeH = zoomOrigH[theID] - startH;

		// Setup Zoom

		zoomCurrent = 0;

		// Setup Fade with Zoom, If Requested

		if (includeFade == 1) {
			fadeCurrent = 0;
			fadeAmount = (100 - 0) / tempSteps;
		} else {
			fadeAmount = 0;
		}

		// Do It!

		zoomTimer[theID] = setInterval("zoomElement('"+zoomID+"', '"+theID+"', "+zoomCurrent+", "+startW+", "+zoomChangeW+", "+startH+", "+zoomChangeH+", "+startX+", "+zoomChangeX+", "+startY+", "+zoomChangeY+", "+tempSteps+", "+includeFade+", "+fadeAmount+", 'zoomDone(zoomID, theID)')", zoomTime);	
		zoomActive[theID] = true;
	}
}

// Finished Zooming In

function zoomDoneIn(zoomdiv, theID) {

	// Note that it's open
  
	zoomOpen = true;
	zoomdiv = document.getElementById(zoomdiv);

	// Position the table shadow behind the zoomed in image, and display it

	if (document.getElementById("ShadowBox")) {

		_setOpacity(0, "ShadowBox");
		shadowdiv = document.getElementById("ShadowBox");

		shadowLeft = parseInt(zoomdiv.style.left) - 13;
		shadowTop = parseInt(zoomdiv.style.top) - 8;
		shadowWidth = zoomdiv.offsetWidth + 26;
		shadowHeight = zoomdiv.offsetHeight + 26; 
	
		shadowdiv.style.width = shadowWidth + 'px';
		shadowdiv.style.height = shadowHeight + 'px';
		shadowdiv.style.left = shadowLeft + 'px';
		shadowdiv.style.top = shadowTop + 'px';

		document.getElementById("ShadowBox").style.visibility = "visible";
		fadeElementSetup("ShadowBox", 0, 100, 5);
		
	} else if (! browserIsIE) {
		// Or, do a fade of the modern shadow
		fadeElementSetup("ZoomImage", 0, .8, 5, 0, "shadow");
	}
	
	// Position and display the CAPTION, if existing
  
	if (includeCaption && document.getElementById(zoomCaption).innerHTML != "") {
		// _setOpacity(0, zoomCaptionDiv);
		zoomcapd = document.getElementById(zoomCaptionDiv);
		zoomcapd.style.top = parseInt(zoomdiv.style.top) + (zoomdiv.offsetHeight + 15) + 'px';
		zoomcapd.style.left = (myWidth / 2) - (zoomcapd.offsetWidth / 2) + 'px';
		zoomcapd.style.visibility = "visible";
		// fadeElementSetup(zoomCaptionDiv, 0, 100, 5);
	}   
	
	// Display Close Box (fade it if it's not IE)

	if (!browserIsIE) _setOpacity(0, "ZoomClose");
	document.getElementById("ZoomClose").style.visibility = "visible";
	if (!browserIsIE) fadeElementSetup("ZoomClose", 0, 100, 5);

	// Get keypresses
	document.onkeypress = getKey;
	
}

// Finished Zooming Out

function zoomDone(zoomdiv, theID) {

	// No longer open
  
	zoomOpen = false;

	// Clear stuff out, clean up

	zoomOrigH[theID] = "";
	zoomOrigW[theID] = "";
	document.getElementById(zoomdiv).style.visibility = "hidden";
	zoomActive[theID] == false;

	// Stop getting keypresses

	document.onkeypress = null;

}

// Actually zoom the element

function zoomElement(zoomdiv, theID, zoomCurrent, zoomStartW, zoomChangeW, zoomStartH, zoomChangeH, zoomStartX, zoomChangeX, zoomStartY, zoomChangeY, zoomSteps, includeFade, fadeAmount, execWhenDone) {

	// console.log("Zooming Step #"+zoomCurrent+ " of "+zoomSteps+" (zoom " + zoomStartW + "/" + zoomChangeW + ") (zoom " + zoomStartH + "/" + zoomChangeH + ")  (zoom " + zoomStartX + "/" + zoomChangeX + ")  (zoom " + zoomStartY + "/" + zoomChangeY + ") Fade: "+fadeAmount);
    
	// Test if we're done, or if we continue

	if (zoomCurrent == (zoomSteps + 1)) {
		zoomActive[theID] = false;
		clearInterval(zoomTimer[theID]);

		if (execWhenDone != "") {
			eval(execWhenDone);
		}
	} else {
	
		// Do the Fade!
	  
		if (includeFade == 1) {
			if (fadeAmount < 0) {
				_setOpacity(Math.abs(zoomCurrent * fadeAmount), zoomdiv);
			} else {
				_setOpacity(100 - (zoomCurrent * fadeAmount), zoomdiv);
			}
		}
	  
		// Calculate this step's difference, and move it!
		
		moveW = cubicInOut(zoomCurrent, zoomStartW, zoomChangeW, zoomSteps);
		moveH = cubicInOut(zoomCurrent, zoomStartH, zoomChangeH, zoomSteps);
		moveX = cubicInOut(zoomCurrent, zoomStartX, zoomChangeX, zoomSteps);
		moveY = cubicInOut(zoomCurrent, zoomStartY, zoomChangeY, zoomSteps);
	
		document.getElementById(zoomdiv).style.left = moveX + 'px';
		document.getElementById(zoomdiv).style.top = moveY + 'px';
		zoomimg.style.width = moveW + 'px';
		zoomimg.style.height = moveH + 'px';
	
		zoomCurrent++;
		
		clearInterval(zoomTimer[theID]);
		zoomTimer[theID] = setInterval("zoomElement('"+zoomdiv+"', '"+theID+"', "+zoomCurrent+", "+zoomStartW+", "+zoomChangeW+", "+zoomStartH+", "+zoomChangeH+", "+zoomStartX+", "+zoomChangeX+", "+zoomStartY+", "+zoomChangeY+", "+zoomSteps+", "+includeFade+", "+fadeAmount+", '"+execWhenDone+"')", zoomTime);
	}
}

// Zoom Utility: Get Key Press when image is open, and act accordingly

function getKey(evt) {
	if (! evt) {
		theKey = event.keyCode;
	} else {
		theKey = evt.keyCode;
	}

	if (theKey == 27) { // ESC
		zoomOut(this, evt);
	}
}

////////////////////////////
//
// FADE Functions
//

function fadeOut(elem) {
	if (elem.id) {
		fadeElementSetup(elem.id, 100, 0, 10);
	}
}

function fadeIn(elem) {
	if (elem.id) {
		fadeElementSetup(elem.id, 0, 100, 10);	
	}
}

// Fade: Initialize the fade function

var fadeActive = new Array();
var fadeQueue  = new Array();
var fadeTimer  = new Array();
var fadeClose  = new Array();
var fadeMode   = new Array();

function fadeElementSetup(theID, fdStart, fdEnd, fdSteps, fdClose, fdMode) {

	// alert("Fading: "+theID+" Steps: "+fdSteps+" Mode: "+fdMode);

	if (fadeActive[theID] == true) {
		// Already animating, queue up this command
		fadeQueue[theID] = new Array(theID, fdStart, fdEnd, fdSteps);
	} else {
		fadeSteps = fdSteps;
		fadeCurrent = 0;
		fadeAmount = (fdStart - fdEnd) / fadeSteps;
		fadeTimer[theID] = setInterval("fadeElement('"+theID+"', '"+fadeCurrent+"', '"+fadeAmount+"', '"+fadeSteps+"')", 15);
		fadeActive[theID] = true;
		fadeMode[theID] = fdMode;
		
		if (fdClose == 1) {
			fadeClose[theID] = true;
		} else {
			fadeClose[theID] = false;
		}
	}
}

// Fade: Do the fade. This function will call itself, modifying the parameters, so
// many instances can run concurrently. Can fade using opacity, or fade using a box-shadow.

function fadeElement(theID, fadeCurrent, fadeAmount, fadeSteps) {

	if (fadeCurrent == fadeSteps) {

		// We're done, so clear.

		clearInterval(fadeTimer[theID]);
		fadeActive[theID] = false;
		fadeTimer[theID] = false;

		// Should we close it once the fade is complete?

		if (fadeClose[theID] == true) {
			document.getElementById(theID).style.visibility = "hidden";
		}

		// Hang on.. did a command queue while we were working? If so, make it happen now

		if (fadeQueue[theID] && fadeQueue[theID] != false) {
			fadeElementSetup(fadeQueue[theID][0], fadeQueue[theID][1], fadeQueue[theID][2], fadeQueue[theID][3]);
			fadeQueue[theID] = false;
		}
	} else {

		fadeCurrent++;
		
		// Now actually do the fade adjustment.
		
		if (fadeMode[theID] == "shadow") {

			// Do a special fade on the webkit-box-shadow of the object
		
			if (fadeAmount < 0) {
				document.getElementById(theID).style.webkitBoxShadow = shadowSettings + (Math.abs(fadeCurrent * fadeAmount)) + ')';
			} else {
				document.getElementById(theID).style.webkitBoxShadow = shadowSettings + (100 - (fadeCurrent * fadeAmount)) + ')';
			}
			
		} else {
		
			// Set the opacity depending on if we're adding or subtracting (pos or neg)
			
			if (fadeAmount < 0) {
				_setOpacity(Math.abs(fadeCurrent * fadeAmount), theID);
			} else {
				_setOpacity(100 - (fadeCurrent * fadeAmount), theID);
			}
		}

		// Keep going, and send myself the updated variables
		clearInterval(fadeTimer[theID]);
		fadeTimer[theID] = setInterval("fadeElement('"+theID+"', '"+fadeCurrent+"', '"+fadeAmount+"', '"+fadeSteps+"')", 15);
	}
}

////////////////////////////
//
// UTILITY functions
//

// Utility: Set the opacity, compatible with a number of browsers. Value from 0 to 100.

function _setOpacity(opacity, theID) {

	var object = document.getElementById(theID).style;

	// If it's 100, set it to 99 for Firefox.

	if (navigator.userAgent.indexOf("Firefox") != -1) {
		if (opacity == 100) { opacity = 99.9999; } // This is majorly awkward
	}

	// Multi-browser opacity setting

	object.filter = "alpha(opacity=" + opacity + ")"; // IE/Win
	object.opacity = (opacity / 100);                 // Safari 1.2, Firefox+Mozilla

}

// Utility: Math functions for animation calucations - From http://www.robertpenner.com/easing/
//
// t = time, b = begin, c = change, d = duration
// time = current frame, begin is fixed, change is basically finish - begin, duration is fixed (frames),

function linear(t, b, c, d)
{
	return c*t/d + b;
}

function sineInOut(t, b, c, d)
{
	return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
}

function cubicIn(t, b, c, d) {
	return c*(t/=d)*t*t + b;
}

function cubicOut(t, b, c, d) {
	return c*((t=t/d-1)*t*t + 1) + b;
}

function cubicInOut(t, b, c, d)
{
	if ((t/=d/2) < 1) return c/2*t*t*t + b;
	return c/2*((t-=2)*t*t + 2) + b;
}

function bounceOut(t, b, c, d)
{
	if ((t/=d) < (1/2.75)){
		return c*(7.5625*t*t) + b;
	} else if (t < (2/2.75)){
		return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
	} else if (t < (2.5/2.75)){
		return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
	} else {
		return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
	}
}


// Utility: Get the size of the window, and set myWidth and myHeight
// Credit to quirksmode.org

function getSize() {

	// Window Size

	if (self.innerHeight) { // Everyone but IE
		myWidth = window.innerWidth;
		myHeight = window.innerHeight;
		myScroll = window.pageYOffset;
	} else if (document.documentElement && document.documentElement.clientHeight) { // IE6 Strict
		myWidth = document.documentElement.clientWidth;
		myHeight = document.documentElement.clientHeight;
		myScroll = document.documentElement.scrollTop;
	} else if (document.body) { // Other IE, such as IE7
		myWidth = document.body.clientWidth;
		myHeight = document.body.clientHeight;
		myScroll = document.body.scrollTop;
	}

	// Page size w/offscreen areas

	if (window.innerHeight && window.scrollMaxY) {	
		myScrollWidth = document.body.scrollWidth;
		myScrollHeight = window.innerHeight + window.scrollMaxY;
	} else if (document.body.scrollHeight > document.body.offsetHeight) { // All but Explorer Mac
		myScrollWidth = document.body.scrollWidth;
		myScrollHeight = document.body.scrollHeight;
	} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
		myScrollWidth = document.body.offsetWidth;
		myScrollHeight = document.body.offsetHeight;
	}
}

// Utility: Get Shift Key Status
// IE events don't seem to get passed through the function, so grab it from the window.

function getShift(evt) {
	var shift = false;
	if (! evt && window.event) {
		shift = window.event.shiftKey;
	} else if (evt) {
		shift = evt.shiftKey;
		if (shift) evt.stopPropagation(); // Prevents Firefox from doing shifty things
	}
	return shift;
}

// Utility: Find the Y position of an element on a page. Return Y and X as an array

function findElementPos(elemFind)
{
	var elemX = 0;
	var elemY = 0;
	do {
		elemX += elemFind.offsetLeft;
		elemY += elemFind.offsetTop;
	} while ( elemFind = elemFind.offsetParent )

	return Array(elemX, elemY);
}

// FancyZoomHTML.js - v1.0
// Used to draw necessary HTML elements for FancyZoom
//
// Copyright (c) 2008 Cabel Sasser / Panic Inc
// All rights reserved.

function insertZoomHTML() {

	// All of this junk creates the three <div>'s used to hold the closebox, image, and zoom shadow.
	
	var inBody = document.getElementsByTagName("body").item(0);
	
	// WAIT SPINNER
	
	var inSpinbox = document.createElement("div");
	inSpinbox.setAttribute('id', 'ZoomSpin');
	inSpinbox.style.position = 'absolute';
	inSpinbox.style.left = '10px';
	inSpinbox.style.top = '10px';
	inSpinbox.style.visibility = 'hidden';
	inSpinbox.style.zIndex = '525';
	inBody.insertBefore(inSpinbox, inBody.firstChild);
	
	var inSpinImage = document.createElement("img");
	inSpinImage.setAttribute('id', 'SpinImage');
	inSpinImage.setAttribute('src', zoomImagesURI+'zoom-spin-1.png');
	inSpinbox.appendChild(inSpinImage);
	
	// ZOOM IMAGE
	//
	// <div id="ZoomBox">
	//   <a href="javascript:zoomOut();"><img src="/images/spacer.gif" id="ZoomImage" border="0"></a> <!-- THE IMAGE -->
	//   <div id="ZoomClose">
	//     <a href="javascript:zoomOut();"><img src="/images/closebox.png" width="30" height="30" border="0"></a>
	//   </div>
	// </div>
	
	var inZoombox = document.createElement("div");
	inZoombox.setAttribute('id', 'ZoomBox');
	
	inZoombox.style.position = 'absolute'; 
	inZoombox.style.left = '10px';
	inZoombox.style.top = '10px';
	inZoombox.style.visibility = 'hidden';
	inZoombox.style.zIndex = '499';
	
	inBody.insertBefore(inZoombox, inSpinbox.nextSibling);
	
	var inImage1 = document.createElement("img");
	inImage1.onclick = function (event) { zoomOut(this, event); return false; };	
	inImage1.setAttribute('src',zoomImagesURI+'spacer.gif');
	inImage1.setAttribute('id','ZoomImage');
	inImage1.setAttribute('border', '0');
	// inImage1.setAttribute('onMouseOver', 'zoomMouseOver();')
	// inImage1.setAttribute('onMouseOut', 'zoomMouseOut();')
	
	// This must be set first, so we can later test it using webkitBoxShadow.
	inImage1.setAttribute('style', '-webkit-box-shadow: '+shadowSettings+'0.0)');
	inImage1.style.display = 'block';
	inImage1.style.width = '10px';
	inImage1.style.height = '10px';
	inImage1.style.cursor = 'pointer'; // -webkit-zoom-out?
	inZoombox.appendChild(inImage1);

	var inClosebox = document.createElement("div");
	inClosebox.setAttribute('id', 'ZoomClose');
	inClosebox.style.position = 'absolute';
	
	// In MSIE, we need to put the close box inside the image.
	// It's 2008 and I'm having to do a browser detect? Sigh.
	if (browserIsIE) {
		inClosebox.style.left = '-1px';
		inClosebox.style.top = '0px';	
	} else {
		inClosebox.style.left = '-15px';
		inClosebox.style.top = '-15px';
	}
	
	inClosebox.style.visibility = 'hidden';
	inZoombox.appendChild(inClosebox);
		
	var inImage2 = document.createElement("img");
	inImage2.onclick = function (event) { zoomOut(this, event); return false; };	
	inImage2.setAttribute('src',zoomImagesURI+'closebox.png');		
	inImage2.setAttribute('width','30');
	inImage2.setAttribute('height','30');
	inImage2.setAttribute('border','0');
	inImage2.style.cursor = 'pointer';		
	inClosebox.appendChild(inImage2);
	
	// SHADOW
	// Only draw the table-based shadow if the programatic webkitBoxShadow fails!
	// Also, don't draw it if we're IE -- it wouldn't look quite right anyway.
	
	if (! document.getElementById('ZoomImage').style.webkitBoxShadow && ! browserIsIE) {

		// SHADOW BASE
		
		var inFixedBox = document.createElement("div");
		inFixedBox.setAttribute('id', 'ShadowBox');
		inFixedBox.style.position = 'absolute'; 
		inFixedBox.style.left = '50px';
		inFixedBox.style.top = '50px';
		inFixedBox.style.width = '100px';
		inFixedBox.style.height = '100px';
		inFixedBox.style.visibility = 'hidden';
		inFixedBox.style.zIndex = '498';
		inBody.insertBefore(inFixedBox, inZoombox.nextSibling);	
	
		// SHADOW
		// Now, the shadow table. Skip if not compatible, or irrevelant with -box-shadow.
		
		// <div id="ShadowBox"><table border="0" width="100%" height="100%" cellpadding="0" cellspacing="0"> X
		//   <tr height="25">
		//   <td width="27"><img src="/images/zoom-shadow1.png" width="27" height="25"></td>
		//   <td background="/images/zoom-shadow2.png">&nbsp;</td>
		//   <td width="27"><img src="/images/zoom-shadow3.png" width="27" height="25"></td>
		//   </tr>
		
		var inShadowTable = document.createElement("table");
		inShadowTable.setAttribute('border', '0');
		inShadowTable.setAttribute('width', '100%');
		inShadowTable.setAttribute('height', '100%');
		inShadowTable.setAttribute('cellpadding', '0');
		inShadowTable.setAttribute('cellspacing', '0');
		inFixedBox.appendChild(inShadowTable);

		var inShadowTbody = document.createElement("tbody");	// Needed for IE (for HTML4).
		inShadowTable.appendChild(inShadowTbody);
		
		var inRow1 = document.createElement("tr");
		inRow1.style.height = '25px';
		inShadowTbody.appendChild(inRow1);
		
		var inCol1 = document.createElement("td");
		inCol1.style.width = '27px';
		inRow1.appendChild(inCol1);  
		var inShadowImg1 = document.createElement("img");
		inShadowImg1.setAttribute('src', zoomImagesURI+'zoom-shadow1.png');
		inShadowImg1.setAttribute('width', '27');
		inShadowImg1.setAttribute('height', '25');
		inShadowImg1.style.display = 'block';
		inCol1.appendChild(inShadowImg1);
		
		var inCol2 = document.createElement("td");
		inCol2.setAttribute('background', zoomImagesURI+'zoom-shadow2.png');
		inRow1.appendChild(inCol2);
		// inCol2.innerHTML = '<img src=';
		var inSpacer1 = document.createElement("img");
		inSpacer1.setAttribute('src',zoomImagesURI+'spacer.gif');
		inSpacer1.setAttribute('height', '1');
		inSpacer1.setAttribute('width', '1');
		inSpacer1.style.display = 'block';
		inCol2.appendChild(inSpacer1);
		
		var inCol3 = document.createElement("td");
		inCol3.style.width = '27px';
		inRow1.appendChild(inCol3);  
		var inShadowImg3 = document.createElement("img");
		inShadowImg3.setAttribute('src', zoomImagesURI+'zoom-shadow3.png');
		inShadowImg3.setAttribute('width', '27');
		inShadowImg3.setAttribute('height', '25');
		inShadowImg3.style.display = 'block';
		inCol3.appendChild(inShadowImg3);
		
		//   <tr>
		//   <td background="/images/zoom-shadow4.png">&nbsp;</td>
		//   <td bgcolor="#ffffff">&nbsp;</td>
		//   <td background="/images/zoom-shadow5.png">&nbsp;</td>
		//   </tr>
		
		inRow2 = document.createElement("tr");
		inShadowTbody.appendChild(inRow2);
		
		var inCol4 = document.createElement("td");
		inCol4.setAttribute('background', zoomImagesURI+'zoom-shadow4.png');
		inRow2.appendChild(inCol4);
		// inCol4.innerHTML = '&nbsp;';
		var inSpacer2 = document.createElement("img");
		inSpacer2.setAttribute('src',zoomImagesURI+'spacer.gif');
		inSpacer2.setAttribute('height', '1');
		inSpacer2.setAttribute('width', '1');
		inSpacer2.style.display = 'block';
		inCol4.appendChild(inSpacer2);
		
		var inCol5 = document.createElement("td");
		inCol5.setAttribute('bgcolor', '#ffffff');
		inRow2.appendChild(inCol5);
		// inCol5.innerHTML = '&nbsp;';
		var inSpacer3 = document.createElement("img");
		inSpacer3.setAttribute('src',zoomImagesURI+'spacer.gif');
		inSpacer3.setAttribute('height', '1');
		inSpacer3.setAttribute('width', '1');
		inSpacer3.style.display = 'block';
		inCol5.appendChild(inSpacer3);
		
		var inCol6 = document.createElement("td");
		inCol6.setAttribute('background', zoomImagesURI+'zoom-shadow5.png');
		inRow2.appendChild(inCol6);
		// inCol6.innerHTML = '&nbsp;';
		var inSpacer4 = document.createElement("img");
		inSpacer4.setAttribute('src',zoomImagesURI+'spacer.gif');
		inSpacer4.setAttribute('height', '1');
		inSpacer4.setAttribute('width', '1');
		inSpacer4.style.display = 'block';
		inCol6.appendChild(inSpacer4);
		
		//   <tr height="26">
		//   <td width="27"><img src="/images/zoom-shadow6.png" width="27" height="26"</td>
		//   <td background="/images/zoom-shadow7.png">&nbsp;</td>
		//   <td width="27"><img src="/images/zoom-shadow8.png" width="27" height="26"></td>
		//   </tr>  
		// </table>
		
		var inRow3 = document.createElement("tr");
		inRow3.style.height = '26px';
		inShadowTbody.appendChild(inRow3);
		
		var inCol7 = document.createElement("td");
		inCol7.style.width = '27px';
		inRow3.appendChild(inCol7);
		var inShadowImg7 = document.createElement("img");
		inShadowImg7.setAttribute('src', zoomImagesURI+'zoom-shadow6.png');
		inShadowImg7.setAttribute('width', '27');
		inShadowImg7.setAttribute('height', '26');
		inShadowImg7.style.display = 'block';
		inCol7.appendChild(inShadowImg7);
		
		var inCol8 = document.createElement("td");
		inCol8.setAttribute('background', zoomImagesURI+'zoom-shadow7.png');
		inRow3.appendChild(inCol8);  
		// inCol8.innerHTML = '&nbsp;';
		var inSpacer5 = document.createElement("img");
		inSpacer5.setAttribute('src',zoomImagesURI+'spacer.gif');
		inSpacer5.setAttribute('height', '1');
		inSpacer5.setAttribute('width', '1');
		inSpacer5.style.display = 'block';
		inCol8.appendChild(inSpacer5);
		
		var inCol9 = document.createElement("td");
		inCol9.style.width = '27px';
		inRow3.appendChild(inCol9);  
		var inShadowImg9 = document.createElement("img");
		inShadowImg9.setAttribute('src', zoomImagesURI+'zoom-shadow8.png');
		inShadowImg9.setAttribute('width', '27');
		inShadowImg9.setAttribute('height', '26');
		inShadowImg9.style.display = 'block';
		inCol9.appendChild(inShadowImg9);
	}

	if (includeCaption) {
	
		// CAPTION
		//
		// <div id="ZoomCapDiv" style="margin-left: 13px; margin-right: 13px;">
		// <table border="1" cellpadding="0" cellspacing="0">
		// <tr height="26">
		// <td><img src="zoom-caption-l.png" width="13" height="26"></td>
		// <td rowspan="3" background="zoom-caption-fill.png"><div id="ZoomCaption"></div></td>
		// <td><img src="zoom-caption-r.png" width="13" height="26"></td>
		// </tr>
		// </table>
		// </div>
		
		var inCapDiv = document.createElement("div");
		inCapDiv.setAttribute('id', 'ZoomCapDiv');
		inCapDiv.style.position = 'absolute'; 		
		inCapDiv.style.visibility = 'hidden';
		inCapDiv.style.marginLeft = 'auto';
		inCapDiv.style.marginRight = 'auto';
		inCapDiv.style.zIndex = '501';

		inBody.insertBefore(inCapDiv, inZoombox.nextSibling);
		
		var inCapTable = document.createElement("table");
		inCapTable.setAttribute('border', '0');
		inCapTable.setAttribute('cellPadding', '0');	// Wow. These honestly need to
		inCapTable.setAttribute('cellSpacing', '0');	// be intercapped to work in IE. WTF?
		inCapDiv.appendChild(inCapTable);
		
		var inTbody = document.createElement("tbody");	// Needed for IE (for HTML4).
		inCapTable.appendChild(inTbody);
		
		var inCapRow1 = document.createElement("tr");
		inTbody.appendChild(inCapRow1);
		
		var inCapCol1 = document.createElement("td");
		inCapCol1.setAttribute('align', 'right');
		inCapRow1.appendChild(inCapCol1);
		var inCapImg1 = document.createElement("img");
		inCapImg1.setAttribute('src', zoomImagesURI+'zoom-caption-l.png');
		inCapImg1.setAttribute('width', '13');
		inCapImg1.setAttribute('height', '26');
		inCapImg1.style.display = 'block';
		inCapCol1.appendChild(inCapImg1);
		
		var inCapCol2 = document.createElement("td");
		inCapCol2.setAttribute('background', zoomImagesURI+'zoom-caption-fill.png');
		inCapCol2.setAttribute('id', 'ZoomCaption');
		inCapCol2.setAttribute('valign', 'middle');
		inCapCol2.style.fontSize = '14px';
		inCapCol2.style.fontFamily = 'Helvetica';
		inCapCol2.style.fontWeight = 'bold';
		inCapCol2.style.color = '#ffffff';
		inCapCol2.style.textShadow = '0px 2px 4px #000000';
		inCapCol2.style.whiteSpace = 'nowrap';
		inCapRow1.appendChild(inCapCol2);
		
		var inCapCol3 = document.createElement("td");
		inCapRow1.appendChild(inCapCol3);
		var inCapImg2 = document.createElement("img");
		inCapImg2.setAttribute('src', zoomImagesURI+'zoom-caption-r.png');
		inCapImg2.setAttribute('width', '13');
		inCapImg2.setAttribute('height', '26');
		inCapImg2.style.display = 'block';
		inCapCol3.appendChild(inCapImg2);
	}
}

