// JavaScript Document

/// declare namespaces
if (!GEM) var GEM = { };

// -=-=- add a trim function to the string object -=-=-=- //
String.prototype.trim = function() {
	a = this.replace(/^\s+/, '');
	return a.replace(/\s+$/, '');
};
//pads left
String.prototype.lpad = function(padString, length) {
	var str = this;
    while (str.length < length)
        str = padString + str;
    return str;
}
 
//pads right
String.prototype.rpad = function(padString, length) {
	var str = this;
    while (str.length < length)
        str = str + padString;
    return str;
}

/// ARRAY CONTAINING A CROSS-REFERENCE OF DECIMAL NOTATION TO FRACTIONAL NOTATION
var fractionDecimals = new Array();
fractionDecimals["000"] = "";
fractionDecimals["125"] = "1/8";
fractionDecimals["250"] = "1/4";
fractionDecimals["375"] = "3/8";
fractionDecimals["500"] = "1/2";
fractionDecimals["625"] = "5/8";
fractionDecimals["750"] = "3/4";
fractionDecimals["875"] = "7/8";

/// IDENTIFIES THE INCREMENT SIZE FOR FRACTION CALCULATIONS...
var fractionIncrement = 125;	/// 125 = 1/8
var decimalPrecision = 3;

var productClassTooltips = {
	A: "Products with an \"A\" classification are standard items used by many customers are are considered our top line items. We generally try to keep stock at all times for these items.",
	B: "Products with a \"B\" classification are also considered to be good parts, but have only a few customers who purchase them and/or have a volatile usage. If you use any \"B\" parts it is imperative that you place purchase orders well in advance to secure supply. ",
	C: "Products with a \"C\" classification are considered to be custom items and will not be kept in stock. Purchase orders for \"C\" items require a cash deposit in advance of production, with full payment due upon delivery. Please be sure to plan your purchase orders for \"C\" item accordingly. ",
	D: "Products with a \"D\" classification are being discontinued. In most cases we have a very similar product that will work as a drop in replacement should you need this part. They are displayed on our website because we still have some stock on them. Call us, and we will make you a DEAL!"
};

if (Debug) {
	alert("'Debug' object already exists!!");	
}
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
/* -=-=-=-=-=-=-=-=-=- DEBUGGING INFO -=-=-=-=-=-=-=-=- */
var Debug = {
	logDOMElement: null,
	_consoleListener: null,
	_listeners: [],
	_history: "",
	_consoleProperties: {
		barHeight: 20,
	},
	_consoleKeyMonitor: function(e) {
		e = (window.event) ? window.event : e;
		
		var keyNum = (window.event) ? e.keyCode : e.which;
		var keyChar = String.fromCharCode(keyNum).toLowerCase();
		var ctrl = e.ctrlKey;
		var alt = e.altKey;
		var shiftKey = e.shiftKey;
		
		var keystrokes = {
			"alt/c": function() { Debug.ClearLog(); },
			"shift/alt/c": function() {  }
		};
		
		/// ALT+C = clear log
		if (e.altKey && keyChar == "c") {
			Debug.ClearLog();
		}
		
		Debug.Log("Console key monitor: '"+keyChar+"'/"+keyNum+" alt="+e.altKey+", shift="+e.shiftKey+", ctrl="+e.ctrlKey);
	},
	initialize: function() {
		
		var urlInfo = parseUrl(document.url);
		
		Debug.Log("WHY NO LOG?");
		
		var showConsoleCookie = getCookie("gemAutoShowDebugConsole");
		
		//alert("Params: "+urlInfo.queryString+" "+urlInfo.getQueryParam("GemShowDebugConsole"));
		//	alert("BLAH!");
		if ((urlInfo.getQueryParam("GemShowDebugConsole") == "1")
				|| (!isEmptyValue(showConsoleCookie) && parseInt(showConsoleCookie) == 1)
				) {
			
			setCookie("gemAutoShowDebugConsole", "1", 0.04);	/// set cookie to expire approx 1 hours (0.04 days)
			document.observe("dom:loaded", function() { 
				Debug.ShowConsole(); 
				
				var consoleMinimized = getCookie("gemDebugConsoleMinimized");
				if (!isEmptyValue(consoleMinimized) && parseInt(consoleMinimized) > 0) {
					Debug.MinimizeConsole();
				}
			});
		}
	},
	Log: function(msg) {
		var timestamp = new Date();
		this._history += msg+"\n";
		this._listeners.each(function(item) { item(msg, timestamp); });
	},
	AddListener: function(myFunc) {
		this._listeners.push(myFunc);
	},
	RemoveListener: function(myFunc) {
		this._listeners = this._listeners.without(myFunc);
	},
	ShowConsole: function(opts) {
	
		//alert("Showing DebugConsole");
		if (!$("debugConsoleContainer")) {
			var bodyDOM = $$("body")[0];
			bodyDOM.appendChild(Builder.node("div", { id: "debugConsoleContainer" }, [ 
										Builder.node("div", { id: "debugConsoleMaximizedContent" }, [
											Builder.node("table", { id:"debugConsoleHeaderBar", border:"0", cellpadding:"2", cellspacing:"0", width:"100%"}, [
												Builder.node("tbody", null, [
													Builder.node("tr", null, [
														Builder.node("td", { align:"left", id:"debugConsoleCaption" }, []),
														Builder.node("td", { align:"right"  }, [
															Builder.node("input", { type:"button", value:"Close", id:"debugConsoleCloseButton", onclick:"Debug.HideConsole();"}),
														//]),
														//Builder.node("td", { align:"right", width: "35" }, [
															Builder.node("input", { type:"button", value:"Clear", id:"debugConsoleClearButton", onclick:"Debug.ClearLog();"}),
														//]),
														//Builder.node("td", { align:"right", width: "45" }, [
															Builder.node("input", { type:"button", value:"Minimize", id:"debugConsoleMinimizeButton", onclick:"Debug.MinimizeConsole();"})
														])
													])
												])
											]),
											Builder.node("pre", { id: "debugConsoleTextHolder" })
										]),
										Builder.node("div", { id: "debugConsoleMinimizedContent" }, [
											Builder.node("table", { id:"debugConsoleMinimizedBar", border:"0", cellpadding:"2", cellspacing:"0", width:"100%"}, [
												Builder.node("tbody", null, [
													Builder.node("tr", null, [
														Builder.node("td", { align:"left", id:"debugConsoleMinimizedCaption" }, []),
														Builder.node("td", { align:"right", width: "35" }, [
															Builder.node("input", { type:"button", value:"Restore", id:"debugConsoleRestoreButton", onclick:"Debug.RestoreConsole();"})
														])
													])
												])
											])
										])
										
										
									])
								);
			
			
		//alert("Showing Debug Console");
		
			$("debugConsoleContainer").setStyle({ 
														position:"fixed", 
														height:"150px",
														right: "0px",
														bottom:"0px",
														width:"500px",
														margin:"0px",
														backgroundColor:"#ffffff",
														border: "1px solid black"
													});
			$("debugConsoleHeaderBar").setStyle({
														backgroundColor:"#ADADAD",
														color: "#ffffff",
														height:"20px"
														/*
														top:"0px",
														left:"0px", 
														position: "absolute"
														*/
													});
			$("debugConsoleTextHolder").setStyle({ 	
														fontFamily:"Monaco, courier",
														fontSize:"10px",	
														overflow:"auto",
														display:"block",
														height:"130px",
														width:"100%",
														top:"10px",		/// for some reason, this element positions about 10px beneath what it should. So, subtract 10px from the top of where it should be...
														position:"absolute", 
														left:"0px",
														padding:"2px"
													});
			$("debugConsoleMaximizedContent").setStyle({
														width:'100%',
														height:'100%',
														position: "absolute",
														top: "0px",
														left: "0px"
													});
			$("debugConsoleMinimizedContent").setStyle({
														width:'100%',
														height:'20px',
														position: "absolute",
														top: "0px",
														left: "0px"
													});
			$("debugConsoleMinimizedBar").setStyle({
														backgroundColor:"#ADADAD",
														color: "#ffffff",
														height:"20px"
													});												
			$("debugConsoleCaption").setStyle({ fontWeight: "bold", color:"#ffffff" });
			$("debugConsoleMinimizedCaption").setStyle({ fontWeight: "bold", color:"#ffffff" });
											
			$("debugConsoleCaption").innerHTML = $("debugConsoleMinimizedCaption").innerHTML = "GEM Debug Console";
		
			$("debugConsoleMinimizedContent").hide();
			
		}
		
		
		
		if (!Debug._consoleListener) {
			Debug._consoleListener = (function(msg, timestamp) { 
				var stamp = timestamp.getFullYear()+"-"+timestamp.getMonth().toString().lpad("0", 2)+"-"+timestamp.getDate().toString().lpad("0", 2)+" "+timestamp.getHours().toString().lpad("0", 2)+":"+timestamp.getMinutes().toString().lpad("0", 2)+":"+timestamp.getSeconds().toString().lpad("0", 2)+"."+timestamp.getMilliseconds().toString().lpad("0", 3);
				$("debugConsoleTextHolder").innerHTML += "&lt;"+stamp+"&gt; "+msg.replace("<", "&lt;").replace(">", "&gt;")+"<br />"; 
				$("debugConsoleTextHolder").scrollTop = $("debugConsoleTextHolder").scrollHeight;	
			});
			Debug.AddListener(Debug._consoleListener);
		}
		
		document.observe("keypress", Debug._consoleKeyMonitor);
		
		$("debugConsoleTextHolder").innerHTML = this._history.replace("\n", "<br />");
		
		Debug.Log("Console Successfully Opened");
	},
	HideConsole: function(opts) {
		this.RemoveListener(Debug._consoleListener);
		this._consoleListener = null;
		$("debugConsoleContainer").remove();
		document.stopObserving("keypress", Debug._consoleKeyMonitor);
		
		eraseCookie("gemAutoShowDebugConsole");
	},
	MinimizeConsole: function() {
		$("debugConsoleContainer").setStyle("height:20px");
		$("debugConsoleMaximizedContent").hide();
		$("debugConsoleMinimizedContent").show();
		
		setCookie("gemDebugConsoleMinimized", "1", 1);
	},
	RestoreConsole: function() {
		$("debugConsoleContainer").setStyle("height:150px");
		$("debugConsoleMaximizedContent").show();
		$("debugConsoleMinimizedContent").hide();
		
		setCookie("gemDebugConsoleMinimized", "0", 1);
	},
	ClearLog: function() {
		this._history = "";
		$("debugConsoleTextHolder").innerHTML = "";
	},
	SetConsoleStyle: function(styleInfo) {
		$("debugConsoleContainer").setStyle(styleInfo);
	}
};
Debug.initialize();


/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
/// -=-=-=-= HELPER FUNCTION TO CONVERT DECIMAL VALUES TO FRACTIONAL VALUES -=-=-=-=-=- //
function gemConvertDecimalToFraction(value) {
	
	/// removes any non-digit, non-period characters from value
	var cleanString = value.toString().replace(/[^\d\.]*/g, "");
	
	if (cleanString.trim() == "" || new Number(cleanString) == 0) {
		return "";	
	}
	
	/// get the position of the decimal point
	var decimalPos = cleanString.indexOf(".");
	
	/// return the cleaned string if no decimal was present
	//if (decimalPos == -1)
	//	return cleanString;
		
	
	
	/// get the decimal part of the number, padded to desired precision
	var decimalValue = 0;
	var wholeNumber = 0;
	
	if (decimalPos == -1) {
		wholeNumber = new Number(cleanString);
		// leave decimal at 0
	}
	else {
		wholeNumber = new Number(cleanString.substr(0, decimalPos));
		decimalValue = new Number(cleanString.substr(decimalPos+1, decimalPrecision).rpad("0", decimalPrecision));	
	}
	
	/// get the fractional string
	var fString = fractionDecimals[decimalValue.toString().rpad("0", decimalPrecision)];
	var intString = "";
	
	/// if for some reason the fractional was not found, then simply re-append the decimal value....
	if (fString === undefined) {
		fString = "."+decimalValue.toString().rpad("0", decimalPrecision);	
		intString = wholeNumber.toString();		
	}
	/// otherwise append the fractional notation
	else if (fString.trim() != "" && wholeNumber > 0) {
		fString = "-"+fString;
		intString = (wholeNumber > 0) ? wholeNumber.toString() : "";
	}
	else if (wholeNumber > 0) {
		intString = wholeNumber.toString();
	}
	/// return a concatenated string
	return intString+fString+"\"";
	
}

function toggleCollapseChildElementWithName(anscestorElement, toggleElementName, addlOptions) {
	if (addlOptions && typeof(addlOptions.disableCollapse) != "undefined" && addlOptions.disableCollapse == true) return;
	
	var elements = anscestorElement.getElementsByName(toggleElementName);
	if (!elements || elements.length == 0) 
		return;
	
	var element = elements[0];
	if (!element)
		return;
	
	var duration = 0.5;
	
	if (addlOptions) {
		if (typeof(addlOptions.duration) != "undefined") duration = addlOptions.duration;
	}
	
	if (typeof(element.isHidden) === "undefined" || !element.isHidden) {
		new Effect.SlideUp(element, { duration: duration });
		element.isHidden = true;
	}
	else {
		new Effect.SlideDown(element, { duration: duration });
		element.isHidden = false;
	}
	
}
function toggleCollapseChildElementWithId(anscestorElement, toggleElementId, addlOptions) {
	if (addlOptions && typeof(addlOptions.disableCollapse) != "undefined" && addlOptions.disableCollapse == true) return;
		
	var element = anscestorElement.getElementById(toggleElementId);
	if (!element)
		return;
	
	var duration = 0.5;
	
	if (addlOptions) {
		if (typeof(addlOptions.duration) != "undefined") duration = addlOptions.duration;
	}
	
	if (typeof(element.isHidden) === "undefined" || !element.isHidden) {
		new Effect.SlideUp(element, { duration: duration });
		element.isHidden = true;
	}
	else {
		new Effect.SlideDown(element, { duration: duration });
		element.isHidden = false;
	}
	
	alert("Completed here...");
}


function portletHideOrShow(portletDivElement) {
	
	//clearLog();
	//logString("Beginning Show/Hide operations for '"+portletDivElement+"'. Type: '"+portletDivElement.nodeName+"' with ID: '"+portletDivElement.id+"'");
	
	var element = null;
	for (var i = 0; i < portletDivElement.childNodes.length; ++i) {
		if (portletDivElement.childNodes[i].id == "divToHide") {
			element = portletDivElement.childNodes[i];
			break;
		}
	}
	
	if (element == null) {
		//alert("Could not find the proper element 'divToHide' to show/hide.");
		
	//	logString("ERROR: Could not find the proper element 'divToHide'. Aborting animations.");
		return;
	}
	
	
	var duration = 0.25;
	
	if (typeof($(element).isHidden) === "undefined" || !$(element).isHidden) {
		new Effect.SlideUp(element, { duration: duration });
		$(element).isHidden = true;
	}
	else {
		new Effect.SlideDown(element, { duration: duration });
		$(element).isHidden = false;
	}
}

function isEmptyValue(value) {
	return (typeof(value) == "undefined" || value === null || (typeof(value.toString) != "undefined" && value.toString() == ""));		/// IE fix: toString method is not defined on some built-in objects (such as the DOM event object) so we need to also test if toString is defined beore calling it.
//	return (Object.isUndefined(value) || typeof(value) == "undefined" || value === null || value.toString() == "");	
}


/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */

function itemDrilldown_SetupDimensionInfo() {
	/// the "pdim" elements are table rows containing 2 cells. first cell is the label for the dimension, and the second cell containing the value.
	/// So, need to retrieve the value from 2nd child node, and either hide the row if blank, or convert decimals to fractions....
	var material = $("pdimValue_class").innerHTML;
	var itemCode = $("pdimClass").innerHTML;
	var length = $("pdimValue_length").innerHTML;
	var width = $("pdimValue_width").innerHTML;
	var height = $("pdimValue_height").innerHTML;
	var mountStyle = $("pdimValue_mountingStyle").innerHTML;
	var fastenerSize = $("pdimValue_fastenerSize").innerHTML;
	var weight = parseFloat($("pdimValue_weight").innerHTML);
	
	var labelCellWidth = "width='100'";
	var class1 = "style='text-align:right; color:<%=getCurrentAttribute('colortheme','color5')%>; padding-right:5px;'";
	var class2 = "style='color:#666666;'";
	
	if (isEmptyValue(material)) $("pdimMaterial").style.display = "none";
	if (isEmptyValue(itemCode)) $("pdimClass").style.display = "none";
	if (isEmptyValue(length)) $("pdimLength").style.display = "none"; else $("pdimValue_length").innerHTML = gemConvertDecimalToFraction(length);
	if (isEmptyValue(width))  $("pdimWidth").style.display = "none"; else $("pdimValue_width").innerHTML = gemConvertDecimalToFraction(width);
	if (isEmptyValue(height))  $("pdimHeight").style.display = "none"; else $("pdimValue_height").innerHTML = gemConvertDecimalToFraction(height);
	if (isEmptyValue(mountStyle))  $("pdimMountingStyle").style.display = "none";
	if (isEmptyValue(fastenerSize))  $("pdimFastenerSize").style.display = "none";
	if (isEmptyValue(weight) || isNaN(weight) || weight == 0) $("pdimWeight").style.display = "none";

}

/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
	THE FOLLOWING METHODS ADD 'EVENT LISTENERS' TO COMMON DOM EVENTS
	-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
function addDocumentOnLoadListener(newListener) {
	if (newListener == null) 
		return;
		
	//document.observe("dom:loaded", newListener);
	//return;
		
		
	/// get the current onload function
	var currentFunction = window.onload;
	
	// define a new function that wraps any existing function plus the new listener to replace....
	var replacementFunction = function() {
		/// check current function is not null, and execute as apprpriate...
		if (currentFunction != null) 
			currentFunction(arguments);
		
		//// execute the new function
		newListener(arguments);
	};
	
	/// now set the body onload event function with the new replacement function...
	
	window.onload = replacementFunction;
	
}

/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
function addDocumentOnUnloadListener(newListener) {
	if (newListener == null) 
		return;
		
	/// get the current onload function
	var currentFunction = window.onunload;
	
	// define a new function that wraps any existing function plus the new listener to replace....
	var replacementFunction = function() {
		/// check current function is not null, and execute as apprpriate...
		if (currentFunction != null) currentFunction(arguments);
		//// execute the new function
		newListener(arguments);
	};
	
	/// now set the body onload event function with the new replacement function...
	window.onunload = replacementFunction;
	
}

/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
function addWindowOnBlurListener(newListener) {
	if (newListener == null) 
		return;
		
	/// get the current onblur function
	var currentFunction = window.onblur;
	
	// define a new function that wraps any existing function plus the new listener to replace....
	var replacementFunction = function() {
		/// check current function is not null, and execute as apprpriate...
		if (currentFunction != null) currentFunction(arguments);
		//// execute the new function
		newListener(arguments);
	};
	
	/// now set the body onblur event function with the new replacement function...
	window.onblur = replacementFunction;
}

/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
function addWindowOnFocusListener(newListener) {
	if (newListener == null) 
		return;
	
	var currentFunction = window.onfocus;
	
	// define a new function that wraps any existing function plus the new listener to replace....
	var replacementFunction = function() {
		/// check current function is not null, and execute as apprpriate...
		if (currentFunction != null) currentFunction(arguments);
		//// execute the new function
		newListener(arguments);
	};
	
	/// now set the body onfocus event function with the new replacement function...
	window.onfocus = replacementFunction;
}



/* -=-=-=-=- THIS FUNCTION SPECIFIC TO HOME PAGE SLIDE SHOW -=-=-=-=-=-= */

function willChangeAdPage(sender, oldPage, newPage) {
	//DebugLog("WillChangeAdPage event handler called for \""+newPage.adTitle+"\"");
	
	if (!newPage.headlineElement)
		return;
		
	var layout = newPage.headlineElement.getLayout();
	
	var toLocation = new Point(layout.get("left"), layout.get("top"));
	var toSize = new Size(layout.get("width")+layout.get("padding-left")+layout.get("padding-right")+layout.get("border-left")+layout.get("border-right"), layout.get("height")+layout.get("padding-top")+layout.get("padding-bottom")+layout.get("border-top")+layout.get("border-bottom"));
														  
	var boxMover = $("headlineBackgroundFocus");
	new Effect.Morph(boxMover, {duration:sender.transitionInterval, style:"left:"+toLocation.x+"px; top:"+toLocation.y+"px; width:"+toSize.width+"px; height:"+toSize.height+"px;" });
	
	return true;
}



/* -=-=-=-=-=-=-=- VARIABLES AND FUNCTIONS USED IN ITEM LIST SORTING -=-=-=-=-=-=-=-=- */

/// FLAG USED TO SKIP 'ONCHANGE' EVENTS ON SORTING DROPDOWN BOX. INIT'S WITH A 'TRUE' VALUE, AND THEN WILL BE CHANGED LATER AFTER THE ONLOAD PROCESSES HAVE FINISHED...
var skipSortingChanged = true;

/// FUNCTION USED TO REDIRECT THE PAGE (OR CHANGE SORTING) WHEN DROPDOWN BOX HAS BEEN CHANGED.
function sortingChanged(element) {
	
	if (skipSortingChanged)
		return;
	
	var selectedValue = element.options[element.selectedIndex].value;
	
	if (!selectedValue || selectedValue == "")
		return;
		
	var sortComponents = [
		selectedValue.substring(0, 1),									// 1st character is T/F string value
		selectedValue.substring(1, selectedValue.length)			// everything after 1st char identifies the 'field' to sort on...
	];
	var newParams = "sort1="+sortComponents[1]+"&sort1desc="+sortComponents[0];
	
	/// get an object containing info about the current URL...
	var urlInfo = parseUrl(document.URL);
	
	for (var paramName in urlInfo.queryParams) {
		if (paramName != "sort1" && paramName != "sort1desc") {
			newParams += "&"+paramName+"="+urlInfo.queryParams[paramName];
		}
	}
	
	var newUrl = urlInfo.filePath+"?"+newParams;
	document.location.href = newUrl;
}

/// FUNCTION THAT SETS THE APPROPRIATE SELECTED VALUE IN THE ITEM SORTING DROPDOWN BOXES. SHOULD BE EXECUTED WHEN DOCUMENT IS FINISHED LOADING.
function onloadSelectSortBoxes() {
	
	var urlInfo = parseUrl(document.URL);
	if (urlInfo.queryParams && urlInfo.queryParams["sort1"])  {
		var selectValue = ((urlInfo.queryParams["sort1desc"]) ? urlInfo.queryParams["sort1desc"] : "F" )+urlInfo.queryParams["sort1"];
		
		$$("select[name=itemSortBox]").each(function(selectNode) {
			
			/// IE bug: It seems that the 'select' DOM element method 'namedItem()' does not seem to function (properly at least) in IE. So we need to loop all options and find the one with the right value we're looking for.
			for (var o = 0; o < selectNode.options.length; ++o) {
				if (selectNode.options[o].value == selectValue) {
					selectNode.options[o].selected = true;
					break;	
				}
			}
		});
	}
	skipSortingChanged = false;
}

/* -=-=-=-=-=-=- COOKIE GET/SET FUNCTIONS =-=-=-=-=-=- (from http://www.quirksmode.org/js/cookies.html) */
function setCookie(name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}

function getCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}

function eraseCookie(name) {
	createCookie(name,"",-1);
}

/* -=-=-=-=-=-=- Debug and Logging Stuff -=-=-=-=-=-=-=-=- */

function GemAddLogListener(aFunction) {
	Debug.AddListener(aFunction);
}
function GemLog(msg) {
	Debug.Log(msg);
}

/* -=-=-=-=- INFO POPOVER (TOOL TIP) FUNCTIONS AND VARIABLES -=-=-=-=-=- */
var TooltipState = {
	NotLoaded: 0,
	Visible: 1,
	Hidden: 2,
	
	DelayedShow: 3,
	DelayedHide: 4,
	
	Showing: 5,
	Hiding: 6
};
var GemTooltipOptions = {

	//// "PUBLIC" properties (it's ok to set these programatically at any time)
	Visible: false,
	X: 0,
	Y: 0,
	FadeInterval: 0.3,
	
	/// "PRIVATE" default properties that should only be hard-coded, and not accessed publically
	_maxWidth: 350,
	_defaultCaption: "Tip",
	_defaultCloseHTML: "X",
	_hideDelay: 0.25,
	_showDelay: 0.1,
	
	/// "PRIVATE" properties that should only be set by the TooltipController class 
	_tooltipLoaded: false,
	_appearEffect: null,
	_hideEffect: null,
	_showTimer: null,
	_hideTimer: null,
	CurrentElement: null,
};


/// THIS IS A 'STATIC' OBJECT. WE DO NOT CREATE INSTANCES OF THE TOOL TIP CONTROLLER.
var TooltipController = {
	_initialize:function() {
		Debug.Log("TooltipController: initializing");
		
	
		if (!isEmptyValue(document) && document.isloaded) {
			this._setupTooltipDOM();		/// if document has completed loading....
		}
		else if (document) {		/// else queue to setup the page once it has completed loading.
			document.observe("dom:loaded", (function() { TooltipController._setupTooltipDOM(); }));
		}
	},
	
	_setupTooltipDOM: function() {
		if (GemTooltipOptions._tooltipLoaded) 
			return;
		
		Debug.Log("TooltipController: Setting up UI.");
		
		//alert("TooltipController LOADING DOM...");
		
		/* THIS CODE CREATES SOME DOM ELEMENTS SIMILAR TO THE FOLLOWING HTML:...
		<div id="tooltipOuterContainer">
			<table border="0" cellpadding="0" cellspacing="0">
				<tbody>
					<tr><td><a id="tooltipCloseLink" href="javascipt:void(0);">Close</a></td></tr>
					<tr>
						<td>
							<div id="tooltipMessageContainer"></div>
						</td>
					</tr>
				</tbody>
			</table>
		</div>
		*/
		
		var bodyDOM = $$("body")[0];
		
		bodyDOM.appendChild(Builder.node("div", { id: "tooltipOuterContainer" }, [
										Builder.node("table", { cellpadding:"0", cellspacing:"0", border:"0" }, [
											Builder.node("tbody", {  }, [
												Builder.node("tr", {  }, [
													Builder.node("td", { id:"tooltipCaption", align:"left", valign:"middle", nowrap:"nowrap"  },  []),
													Builder.node("td", { align: "right", valign: "middle", id:"tooltipHeaderBar", nowrap:"nowrap" }, [
														Builder.node("a", { href:"javascript:void(0);", id:"tooltipCloseLink"  }, [])
													])
												]), 
												Builder.node("tr", {  }, [
													Builder.node("td", { colspan: "2", align: "left", valign: "top" }, [
														Builder.node("div", { id: "tooltipMessageContainer" }, [])
													])
												])
											])
										])	
								]));
								
								
		$("tooltipOuterContainer").hide();
		$("tooltipCloseLink").innerHTML = "Close";
		$("tooltipCloseLink").onclick = (function(e) { TooltipController.Hide({ delay:0.0 }); });
		$("tooltipCaption").innerHTML = "Tooltip";
		
		
		$("tooltipOuterContainer").onmouseover = (function(e) {
			clearTimeout(GemTooltipOptions._hideTimer);
		});
		$("tooltipOuterContainer").onmouseout = (function(e) {
			TooltipController.Hide();
		});


		GemTooltipOptions._tooltipLoaded = true;
	},
	Show: function(msgOrElement, opts) {
		if (!GemTooltipOptions._tooltipLoaded) {
		//	alert("TooltipController.showTooltip - tooltip has not been loaded");
			Debug.Log("TooltipController: can't show tooltip because the UI hasn't been loaded.");
			return;
		}
			
		/* Parameters:
			- msgOrElement (required): a string or DOM element used to fill the contents of the tooltip message.
			- opts (required):
				- Required props:
					- x: numeric property indicating the left placement of on the page AND...
					- y: numeric property indicating the top placement on the page
					OR
					- eventInfo: DOM event property during events for getting the x/y properties of the mouse....
				- Optional props:
					- caption/captionText:	string that specifies text to use in the caption/title of the tooltip
					- maxWidth: string that specifies the maximum width to use for the tooltip (integer number/string only, no CSS units)
					- closeText: string or HTML that will be used for the contents of the "close" link 
					- animated: bool value indicating whether to animate appearance of the tip, default = true
					- delay: numeric value indicated how long to delay the appearance of the tip, default = GemTooltipOptions._showDelay
		*/


		
		var myOpts = Object.extend({
				x: 0,
				y: 0,
				eventInfo:null,
				caption: GemTooltipOptions._defaultCaption,
				maxWidth: GemTooltipOptions._maxWidth,
				closeText: GemTooltipOptions._defaultCloseHTML,
				animated: true,
				delay: GemTooltipOptions._showDelay,
				element: null
			}, opts || { });
		
		if (!isEmptyValue(opts.x) && !isEmptyValue(opts.y)) {
			/// X and Y properties were already set/extended above
		}
		else if (!isEmptyValue(opts.eventInfo)) {
			var scrollOffsets = document.viewport.getScrollOffsets();
			Object.extend(myOpts, { x: opts.eventInfo.clientX+scrollOffsets[0],	y: opts.eventInfo.clientY+scrollOffsets[1] });
		}
		else {
			throw "The function TooltipController.Show(msgOrElement, opts) requires an 'x' and 'y' property to be defined (OR an 'eventInfo' property) in the 'opts' parameter.";
		}
		
		/*
		if (GemTooltipOptions.Visible && !isEmptyValue(GemTooltipOptions.CurrentElement) && !isEmptyValue(myOpts.element) && GemTooltipOptions.CurrentElement == myOpts.element) {
			Debug.Log("TooltipController: skipping show operations because we're trying to show again for the current element.");
			return;	//// do nothing if trying to show the same tooltip again...
		}
		*/
		
		if (isEmptyValue(myOpts.caption) && !isEmptyValue(myOpts.captionText))
			Object.extend(myOpts, { caption: myOpts.captionText });
		
		
		
		Debug.Log("TooltipController: Show '"+myOpts.caption+"'. Animated: "+myOpts.animated);
		
		TooltipController._clearCurrentOperations();		
		
		var showOperations = (function() {
			
			if (Object.isElement(msgOrElement)) {
				$A($("tooltipMessageContainer").childNodes).each(function(node) { $("tooltipMessageContainer").removeChild(node); } );	/// clears all children nodes
				$("tooltipMessageContainer").appendChild(msgOrElement);																						/// adds new element as child node...
			}
			else {
				$("tooltipMessageContainer").innerHTML = msgOrElement;
			}
			
			$("tooltipCaption").innerHTML = myOpts.caption;
			$("tooltipCloseLink").innerHTML = myOpts.closeText;
			$("tooltipOuterContainer").setStyle({ left: myOpts.x.toString()+"px", top:myOpts.y.toString()+"px", maxWidth: myOpts.maxWidth+"px" });
			
			
			/// ensure that the tooltip is not going to be shown 'off the screen'
			var layout = $("tooltipOuterContainer").getLayout();
			var scrollOffsets = document.viewport.getScrollOffsets();
			var viewportSize = document.viewport.getDimensions();
			
			var containerRect = {
				left: myOpts.x,
				top: myOpts.y,
				right: myOpts.x + layout.get("width"),
				bottom: myOpts.y + layout.get("height"),
				width: layout.get("width"),
				height: layout.get("height")
			};
			var pageRect = {
				width: viewportSize.width,
				height: viewportSize.height,
				offsetLeft: scrollOffsets.left,
				offsetTop: scrollOffsets.top,
				offsetRight: scrollOffsets.left + viewportSize.width,
				offsetBottom: scrollOffsets.top + viewportSize.height
			};
			
			var diffX = diffY = 0;
			if (containerRect.right > pageRect.offsetRight) {
				diffX = containerRect.right - pageRect.offsetRight;
				diffX = (diffX < 0) ? containerRect.left : diffX;
			}
			if (containerRect.bottom > pageRect.offsetBottom) {
				diffY = containerRect.bottom - pageRect.offsetBottom;
				diffY = (diffY < 0) ? containerRect.top : diffY;
			}
			
			if (diffX != 0 || diffY != 0) {
				myOpts.x -= diffX;
				myOpts.y -= diffY;		
				$("tooltipOuterContainer").setStyle({ left: myOpts.x.toString()+"px", top:myOpts.y.toString()+"px"  });	
				
			}
			
			if (myOpts.animated)
				GemTooltipOptions._appearEffect = new Effect.Appear($("tooltipOuterContainer"), { duration:GemTooltipOptions.FadeInterval, from:0.0, to: 1.0, afterFinish: (function() { GemTooltipOptions._appearEffect = null; }) });	
			else 
				$("tooltipOuterContainer").show();
			
			GemTooltipOptions.X = myOpts.x;
			GemTooltipOptions.Y = myOpts.y;
			GemTooltipOptions.Visible = true;
			GemTooltipOptions.CurrentElement = myOpts.element;
				
		//	Debug.Log("TooltipController: Finished Show Operations");
		});
		
		
		if (myOpts.delay > 0) 
			GemTooltipOptions._showTimer = setTimeout(showOperations, myOpts.delay*1000);
		else
			showOperations();
		
	},
	Hide: function(opts) {
		if (!GemTooltipOptions._tooltipLoaded) {
			Debug.Log("TooltipController: can't hide tooltip because the UI hasn't been loaded.");
			return;
		}
		
		Debug.Log("TooltipController: Hide");
		
		var myOpts = Object.extend({
									delay: GemTooltipOptions._hideDelay,
									animated: true
								}, opts || {});
								
		TooltipController._clearCurrentOperations();
		
		var hideOperations = (function() {
				if (myOpts.animated) {
					GemTooltipOptions._hideEffect = new Effect.Fade($("tooltipOuterContainer"), {duration: GemTooltipOptions.FadeInterval, afterFinish: (function() { GemTooltipOptions._hideEffect = null; }) });	
				}
				else {
					$("tooltipOuterContainer").hide();
				}
				GemTooltipOptions.Visible = false;
				GemTooltipOptions.CurrentElement = null;
			});
		
		if (myOpts.delay > 0) {
			GemTooltipOptions._hideTimer = setTimeout(hideOperations, myOpts.delay*1000);
		}
		else {
			hideOperations();
		}
	},
	_clearCurrentOperations: function() {
		if (GemTooltipOptions._showTimer) {
			clearTimeout(GemTooltipOptions._showTimer);
			GemTooltipOptions._showTimer = null;
		}
		if (GemTooltipOptions._appearEffect) {
			GemTooltipOptions._appearEffect.cancel();
			GemTooltipOptions._appearEffect = null;
		}
		if (GemTooltipOptions._hideTimer) {
			clearTimeout(GemTooltipOptions._hideTimer);
			GemTooltipOptions._hideTimer = null;
		}
		if (GemTooltipOptions._hideEffect) {
			GemTooltipOptions._hideEffect.cancel();
			GemTooltipOptions._hideEffect = null;
		}
	},
	/// Helper function that attaches a tooltip to a DOM element by configuring mouse events.
	SetupElementForMouseOverTooltip: function(element, msg, addlOpts) {
		element.onmouseover = element.onclick = function(e) {  
				Debug.Log("Tooltip: Mouseover");
				 try { 
					var ev = (e) ? e : window.event;
					if (ev.target != this)
						return;
					
					var opts = { eventInfo: ev, element: this };
					 if (!isEmptyValue(addlOpts)) {
						 Object.extend(opts, addlOpts);
					 }
					TooltipController.Show(msg, opts);
				 } 
				 catch(ex) {   }
			};
		element.onmouseout = function(e) { 
			Debug.Log("Tooltip: Mouseout");
			try { 
					var ev = (e) ? e : window.event;
					if (ev.target != this)
						return;
					TooltipController.Hide(); 
			} 
			catch(ex) {  }
		 };
	},
	SetupElementForClickTooltip: function(element, msg, addlOpts) {
		element.style.cursor = "pointer";
		element.onclick = function(e) { 
				Debug.Log("Tooltip: Click");
				 try { 
					var ev = (e) ? e : window.event;
					var opts = { eventInfo: ev, element: this };
					 if (!isEmptyValue(addlOpts)) {
						 Object.extend(opts, addlOpts);
					 }
					TooltipController.Show(msg, opts);
				 } 
				 catch(ex) {   }
			};
	}
};
TooltipController._initialize();


/* -=-=-=- TOOLTIP FUNCTIONS SPECIFIC TO PRODUCT CLASSES -=-=-=-=-=- */
function getProductClassTooltip(className) {
	/// take the first letter of name and return the long description.
	if (className.length == 0)
		return "";
		
	return productClassTooltips[className.substring(0, 1).toUpperCase()];	
}
function loadProductClassTooltips() {
	var elements = $$("[rel=productclasstooltip][pclass]");
	elements.each(function(node) { 
		var productClass = node.getAttribute("pclass");
		var infoSpan = Builder.node("span");
		infoSpan.innerHTML = getProductClassTooltip(productClass);
		var linkToPage = Builder.node("a", { href: "http://www.gemlux.com/Product-Classes", title:"Click for more info on product classes.", target: "_blank"});
		linkToPage.innerHTML = "More Information about product classes";
		var container = Builder.node("div", null, [
																infoSpan,
																Builder.node("br"),
																Builder.node("br"),
																linkToPage
												]);
		TooltipController.SetupElementForClickTooltip(node, container, { caption: "Class "+productClass, element: node, maxWidth: 350 });
	});	
}
/// SETUP THE PRODUCT CLASS TOOLTIPS ONCE DOM HAS FINISHED LOADING
document.observe("dom:loaded", (function() { loadProductClassTooltips(); }) );


/// GENERAL HELPER FUNCTION THAT SETS UP MOUSEOVER AND MOUSEOUT EVENTS ON THE SPECIFIED ELEMENT THAT WILL POPUP A TOOLTIP
function gemSetupElementForMouseoverTooltip(element, msg, addlOpts) {
	TooltipController.SetupElementForMouseOverTooltip(element, msg, addlOpts);	
}

/* -=-=-=-=-=-=- TEXT INPUT PLACEHOLDERS -=-=-=-=-=- */
/// THIS FUNCTION SCANS THE PAGE AND SETS UP TEXT INPUTS WITH THE 'placeholder' ATTRIBUTE DEFINED TO SHOW PLACEHOLDER TEXT.
function activatePlaceholders() {
	
	/// safari already supports placeholder attribute
	var detect = navigator.userAgent.toLowerCase(); 
	if (detect.indexOf("safari") > 0) return false;

	var inputs = $$("input[type=text][placeholder]");		/// get array of elements with placeholder attributes
	
	var placeholder = null;
	/// loop the elements
	for (var i=0;i<inputs.length;i++) {
		placeholder = inputs[i].getAttribute("placeholder");
		if (placeholder.length > 0) {
			inputs[i].value = placeholder;
			inputs[i].className = "placeholder";
			inputs[i].onfocus = function() {
				if (this.value == this.getAttribute("placeholder")) {
					this.value = ""; this.className = "";
				}
				return false;
			};
			inputs[i].onblur = function() {
				if (this.value.length < 1) {
					this.value = this.getAttribute("placeholder"); this.className = "placeholder";
				}
			};
		}
	}
}

document.observe("dom:loaded", function() { activatePlaceholders(); });


