/* Create a name space for JavaScript used by the store. This way, we'll be less
 * likely to step on any other libraries and code. */

var STORE = {
	tabs : {},
	utils : {},
	format : {},
	cookie : {},
	ui : {},
	order : {},
	form : {},
	admin : {
		ordering : {},
		// NOTE: import is a reserved word in IE.
		importer : {}
	},
	yui : {}
};

/* Initializes the store. Meant to be called from the document head after
 * loading YUI and the store JavaScript.
 *  
 *   @path - Path to store root directory. Must end in a trailing slash.
 */
STORE.init = function(path)
{
	// Create a YUI configuration generator which can be used for the duration
	// of the request.
	
	yuiRoot = path + "javaScripts/yui/";
	STORE.yui.loader = STORE.yui.createLoader(yuiRoot);
	
	// Handle the domready event.
	
	// NOTE: This will fail unless the dom and event libraries have been
	//       included inline (as opposed to injected by the loader). YUI 3 has
	//       an "injected" property which seems to address this issue. YUI 2
	//       doesn't seem to have a workaround.
	
	YAHOO.util.Event.onDOMReady(STORE.tabs.embelish);
};

STORE.tabs.tabViews = {};

STORE.tabs.embelish = function()
{
	STORE.yui.load(["selector"], function()
	{
		var tabs = YAHOO.util.Selector.query("div.autotab");
		
		for (var i = 0; i < tabs.length; i++)
		{
			var tab = tabs[i];
			STORE.tabs.tabify(tab);
		}
	});
};

STORE.tabs.tabify = function(element)
{
	STORE.yui.load(["store-tabs"], function()
	{
		var tabView = new YAHOO.widget.TabView(element);
		STORE.tabs.tabViews[element.id] = tabView;
	});
};

STORE.utils.fileInputCount = 0;

STORE.utils.appendFileInput = function(divId, name)
{
	var div = document.getElementById(divId);
	STORE.utils.fileInputCount += 1;
	
	var file = document.createElement("input");
	file.type = "file";
	file.maxlength = "255";
	file.name = name + "_" + STORE.utils.fileInputCount;
	file.className = "url";
	
	var caption = document.createElement("input");
	caption.type = "text";
	caption.name = name + "Caption_" + STORE.utils.fileInputCount;
	caption.className = "text";
	
	var br1 = document.createElement("br");
	
	var table = document.createElement("table");
	var tr1 = document.createElement("tr");
	var tr2 = document.createElement("tr");
	var td1 = document.createElement("td");
	var td2 = document.createElement("td");
	var td3 = document.createElement("td");
	var td4 = document.createElement("td");
	var tbody = document.createElement("tbody");
	
	td1.innerHTML = "File:";
	td2.appendChild(file);
					
	td3.innerHTML = "Caption:";
	td4.appendChild(caption);
	
	tr1.appendChild(td1);
	tr1.appendChild(td2);
	tbody.appendChild(tr1);
	
	tr2.appendChild(td3);
	tr2.appendChild(td4);
	tbody.appendChild(tr2);
	
	table.appendChild(tbody);
	
	div.appendChild(table);
	div.appendChild(br1);
};

/*** Formatting ***************************************************************/

/**
 * Formats a float as money.
 * 
 * @value {float} Value to be formatted
 */
STORE.format.money = function(value)
{
	// Round off to the second decimal place. We can't just call toFixed()
	// because it always rounds down.
	
	value = (Math.round(value * 100) / 100).toFixed(2);
	
	var formatted = STORE.format.commas(value);
	
	return "$" + formatted;
};

/**
 * Formats a float with commas.
 * 
 * @value {float} Value to be formatted
 */
STORE.format.commas = function(value)
{
	var original = value.toString();
	
	var regEx = /^(.* )?([-+]?\d+)(\d{3}\b)/;
	var formatted = original.replace(regEx, "$1$2,$3");

	if (formatted !== original)
	{
		formatted = STORE.format.commas(formatted);
	}
	
	return formatted;
};

STORE.format.toNumeric = function(value)
{
	if (typeof value === "undefined" || value == null)
	{
		return "";
	}
	
	return YAHOO.lang.trim(value.replace("$", "").replace(",", ""));
};

STORE.format.toInt = function(value)
{
	return parseInt(STORE.format.toNumeric(value), 10);
};

STORE.format.toFloat = function(value)
{
	return parseFloat(STORE.format.toNumeric(value));
};

/*** Cookie *******************************************************************/

// Returns the value of the cookie if the cookie exists. Otherwise, it returns
// null.
	
STORE.cookie.getCookie = function(cookieName)
{
	var cookies = document.cookie;
	
	var startOfName = cookies.indexOf(cookieName + '=');
	
	if (startOfName < 0)
	{
		return null;
	}
	
	var startOfValue = startOfName + cookieName.length + 1;
	
	var endOfValue = cookies.indexOf(";", startOfValue);
	
	if (endOfValue == -1)
	{
		endOfValue = cookies.length;
	}
	
	var value = cookies.substring(startOfValue, endOfValue);
	
	value = unescape(value);
	
	if (value == "")
	{
		return null;
	}
	
	return value;
};

STORE.cookie.setCookie = function(name, value, expires, domain, path, secure)
{
	// First, deal with the name/value pair. These are separated by an equals
	// sign.
	
	var cookie = name + "=" + escape(value);
	
	// Now, create a function which appends parameters to our cookie.
	
	var append = function(name, value)
	{
		cookie += ";" + name;
		
		// The check for an empty string is to address the path issue mentioned
		// below.
		
		if (value || value == "")
		{
			cookie += "=" + value;
		}
	}
	
	if (expires)
	{
		append("expires", expires.toGMTString());
	}
	
	if (domain)
	{
		append("domain", domain);
	}
	
	if (path)
	{
		append("path", path);
	}
	else if (path == "")
	{
		// If the path is an empty string, then we want to render it as path=
		// to match the behavior of ASP. Internet Explorer is picky about path
		// decarations. The absense of a path attribute is not the same as a
		// path=.
		
		append("path", "");
	}
	
	// The secure attribute doesn't have a value.
	
	if (secure)
	{
		append("secure");
	}
	
	document.cookie = cookie;
};

STORE.cookie.deleteCookie = function(name, domain, path)
{
	var expires = new Date();
	
	STORE.cookie.setCookie(name, "", expires, domain, path);
};

STORE.cookie.supportsCookies = function()
{
	var name = "ThisIsATesT";
	
	var value = "Yes";
	
	STORE.cookie.setCookie(name, value);
	
	var retrievedValue = STORE.cookie.getCookie(name);
	
	if (retrievedValue == value)
	{
		STORE.cookie.deleteCookie(name);
		
		return true;
	}
	else
	{
		return false;
	}
};

STORE.ui.setSelectedTab = function (id, index)
{
	var cookie = STORE.cookie.getCookie("selectedTab") || "";
	
	states = STORE.ui.parseTabStates(cookie);
	
	states[id] = index;
	
	var encodedStates = STORE.ui.encodeTabStates(states);
	
	STORE.cookie.setCookie("selectedTab", encodedStates);	
};

/**
 * Takes a string in the following format:
 *   
 *   363412CF62B693DD578FA3285A4B6C78.2:2,40D96DFBEF7C82448A1B94FA0948CFFB.2:2
 *   
 * And converts into a hash table:
 *  
 *   {
 *     "363412CF62B693DD578FA3285A4B6C78.2" : "2",
 *     "40D96DFBEF7C82448A1B94FA0948CFFB.2" : "2"
 *   }
 */
STORE.ui.parseTabStates = function (encodedString)
{
	var states = {};
	
	// First, convert the string into an array of encoded states. That will give
	// us something that looks like this:
	// 
	//   ["363412CF62B693DD578FA3285A4B6C78.2:2", "40D96DFBEF7C82448A1B94FA0948CFFB.2:2"]
	
	var encodedStates = encodedString.split(",");
	
	for (var i in encodedStates)
	{
		// Each individual state pair will look something like this:
		//   
		//   363412CF62B693DD578FA3285A4B6C78.2:2
		
		var encodedStatePair = encodedStates[i];
		
		// Split the pair so that we can reference each half by index. That will
		// give us something like this:
		//   
		//   ["363412CF62B693DD578FA3285A4B6C78.2", "2"]
		
		var statePair = encodedStatePair.split(":");
		
		// The first element is the ID; the second, the selected tab index.
		
		var id = statePair[0];
		
		var index = statePair[1];
		
		// Add the state to our hash table.
		
		states[id] = index;	
	}
	
	return states;
};

STORE.ui.encodeTabStates = function (states)
{
	var encodedStates = [];
	
	for (var id in states)
	{
		var encodedState = id + ":" + states[id];
		
		encodedStates.push(encodedState);
	}
	
	var encodedStatesAsString = encodedStates.join(",");
	
	return encodedStatesAsString;
};

STORE.ui.panels = {};

STORE.ui.registerPanel = function(panelID, bodyID, linkID, url, options,
		callback)
{
	STORE.yui.load(["store-panel"], function()
	{
		options = options || {};
		options = {
			"width" : options.width || "600px",
			"height" : options.height || "400px",
			"close" : true,
			"visible" : false,
			"fixedcenter" : true,
			"draggable" : false,
			"modal" : false,
			"constraintoviewport" : true,
			"effect" : {
				"effect" : YAHOO.widget.ContainerEffect.FADE,
				"duration" : 0.25
			}
		};
		
		var panel = new YAHOO.widget.Panel(panelID, options);
		
		// Store a panel reference in case we need to hide the panel in the
		// future.
		
		STORE.ui.panels[panelID] = panel;
		
		// NOTE: If the panels are not located directly under the body tag,
		//       errors may occur in IE when calling this during the onload or
		//       domready events. In which case, render should be called
		//       immediately before show().
		
		var E = YAHOO.util.Event;
		
		panel.render();
		
		var link = document.getElementById(linkID);
		
		if (url)
		{
			E.addListener(link, "click", function(e)
			{
				YAHOO.util.Connect.asyncRequest("GET", url,
				{
					"success" : callback,
					"failure" : callback,
					"argument": { "id" : bodyID }
				});
			});
		}
		
		E.addListener(link, "click", function(e)
		{
			panel.show(panel, true);
			// The panel is hidden by default to avoid scroll bars in IE. We
			// want to wait until the panel has been positioned before
			// displaying it.
			document.getElementById(panelID).style.display = "block";
			e.cancelBubble = true;
		});
	});
};

/*** Order ********************************************************************/

STORE.order.stateVisible = true;

STORE.order.handleCountrySelection = function(country, state, province)
{
	country = document.getElementById(country);
	state = document.getElementById(state);
	province = document.getElementById(province);
	
	var countryCode = country.options[country.selectedIndex].value;
	
	if (STORE.order.countryHasStates(countryCode))
	{
		STORE.order.populateStates(countryCode, state);
		
		STORE.order.showState(state, province);
	}
	else
	{
		STORE.order.showProvince(state, province);
	}
};

STORE.order.showState = function(state, province)
{
	state.style.display = "";
	province.style.display = "none";
	
	state.name = state.id;
	province.name = state.id & "-disabled";
	
	STORE.order.stateVisible = true;
};

STORE.order.showProvince = function(state, province)
{
	province.style.display = "";
	state.style.display = "none";

	state.name = state.id + "-disabled";
	province.name = state.id;

	STORE.order.stateVisible = false;
};


STORE.order.countries = {};

STORE.order.countryHasStates = function(country)
{
	if (STORE.order.countries[country])
	{
		return true;
	}
	else
	{
		return false;
	}
};

STORE.order.populateStates = function(countryCode, stateSelectList)
{
	STORE.form.clearSelectList(stateSelectList);
	
	var states = STORE.order.countries[countryCode];
	
	for (var i = 0; i < states.length; i++)
	{
		var state = states[i];
		
		var option = new Option(state.Name, state.Abbreviation);
		
		stateSelectList.options[i] = option;	
	}
};

STORE.order.setState = function(stateSelectList, provinceText, stateName)
{
	if (STORE.order.stateVisible)
	{
		stateSelectList = document.getElementById(stateSelectList);
		
		STORE.form.selectOption(stateSelectList, stateName);
	}
	else
	{
		provinceText = document.getElementById(provinceText);
		
		provinceText.value = stateName;	
	}
};

STORE.order.updateInput = function(id, value)
{
	var div = document.getElementById(id);
	
	div.value = value;
};


/*** Form *********************************************************************/

STORE.form.clearSelectList = function(selectList)
{
	for (var i = selectList.length - 1; i >= 0; i--)
	{
		selectList.options[i] = null;
	}
};


STORE.form.selectOption = function(selectList, optionValue)
{
	for (var i = 0; i < selectList.length; i++)
	{	
		var option = selectList.options[i];
		
		if (option.value == optionValue)
		{
			option.selected = true;
		}
		
	}
};

/*** Admin - Ordering *********************************************************/

STORE.admin.ordering.creditCardTypeCount = 0;
STORE.admin.ordering.creditCardTypesId = "creditCardTypes";

STORE.admin.ordering.addCreditCardType = function()
{
	STORE.admin.ordering.creditCardTypeCount = STORE.admin.ordering.creditCardTypeCount + 1;
	
	var i = STORE.admin.ordering.creditCardTypeCount; // For convenience
	
	// Create the text box
	
	var inputText = document.createElement('input');
	inputText.setAttribute('type', 'text');
	inputText.setAttribute('name', 'newCreditCardType_' + i);
	
	// Create the "(Remove)" link
	
	var removeLink = document.createElement('a');
	removeLink.setAttribute('href', 'javascript:STORE.admin.ordering.removeNewCreditCardType(' + i + ')');
	removeLink.appendChild(document.createTextNode('(Remove)'));
	
	// Create the hidden form field
	
	var hidden = document.createElement('input');
	hidden.setAttribute('type', 'hidden');
	hidden.setAttribute('name', 'newCreditCardTypes');
	hidden.setAttribute('value', i);
	
	// Package the whole thing into a div
	
	var creditCardTypeDiv = document.createElement('div');
	creditCardTypeDiv.setAttribute('id', 'newCreditCardType_' + i);
	creditCardTypeDiv.appendChild(inputText);
	creditCardTypeDiv.appendChild(hidden);
	creditCardTypeDiv.appendChild(removeLink);
		
	// Add that div to the parent element
	
	var element = document.getElementById(STORE.admin.ordering.creditCardTypesId);
	element.appendChild(creditCardTypeDiv);
};

STORE.admin.ordering.removeCreditCardType = function(i)
{
	var creditCardTypeDiv = document.getElementById(STORE.admin.ordering.creditCardTypesId);
	var div = document.getElementById('creditCardTypeDiv_' + i);
	
	creditCardTypeDiv.removeChild(div);
};

STORE.admin.ordering.removeNewCreditCardType = function(i)
{
	var creditCardTypeDiv = document.getElementById(STORE.admin.ordering.creditCardTypesId);
	var div = document.getElementById('newCreditCardType_' + i);
	
	creditCardTypeDiv.removeChild(div);
};

/*** Admin - Import ***********************************************************/

STORE.admin.importer.toggleHelp = function(link)
{
	var help = STORE.admin.importer.getHelpForLink(link);
	
	if (help.style.display != "block")
	{
		help.style.display = "block";
	}
	else
	{
		help.style.display = "none";
	}
};

STORE.admin.importer.getHelpForLink = function(link)
{
	if (link.help)
	{
		return link.help;
	}
	
	var reason = link.getAttribute("store:help");
	
	var id = STORE.admin.importer.getHelpID(reason);
	
	var help = document.getElementById(id);
	
	if (!help)
	{
		help = STORE.admin.importer.getDefaultHelp();
	}
	
	link.help = STORE.admin.importer.copyHelp(help);
	
	link.parentNode.appendChild(link.help); 
	
	return link.help;
};

STORE.admin.importer.copyHelp = function(help)
{
	var clone = document.createElement("DIV");
	
	clone.className = help.className;
	
	clone.innerHTML = help.innerHTML;
	
	return clone;
};

STORE.admin.importer.getHelpID = function(reason)
{
	if (!reason)
	{
		return "import-help-unknown";
	}
	
	return "import-help-" + reason.toLowerCase();
};

STORE.admin.importer.CLASS_NAME = "itemHelp";

STORE.admin.importer.getDefaultHelp = function()
{
	var help = document.createElement("DIV");;
	
	help.className = STORE.admin.importer.CLASS_NAME;
	
	help.innerHTML = "<p>No help available for this item.</p>";
	
	return help;
};

/*** YUI **********************************************************************/

/**
 * Loads zero or more libraries and invokes the specified callback.
 */
STORE.yui.load = function(libraries, callback)
{
	if (!libraries || !libraries.length)
	{
		callback();
	}
	
	// Record this callback.
	
	// FIXME: Potential race condition. It's possible that a previous insert()
	//        event will complete between the call to push() and the call to
	//        insert(). In which case, the callback will get processed as part
	//        of the previous request.
	
	STORE.yui.callbacks.push(callback);
	
	STORE.yui.loader.insert({
		require: libraries,
		onSuccess: function()
		{
			// Slice the array to make a copy. Then, splice the original array.
			// This way, we avoid a race condition (i.e. someone adding a
			// callback between the operations).
			
			var count = STORE.yui.callbacks.length;
			var callbacks = STORE.yui.callbacks.slice(0, count);
			STORE.yui.callbacks.splice(0, count);
			
			// Process the callbacks.
			
			while (callbacks.length > 0)
			{
				var callback = callbacks.shift();
				callback();
			}
		},
		onFailure: function(ex) { throw "YUI loader failure. " + ex.msg; }
	});
};

/**
 * Outstanding callbacks. The YUI loader does not handle multiple calls to
 * insert well. It will only fire onSuccess once for all outstanding requests,
 * not once for each call to insert().
 */
STORE.yui.callbacks = [];

/**
 * YUI 2 loader.
 */
STORE.yui.loader = null;

/**
 * Creates and configures a YUI 2 loader object.
 * 
 * @param {string} path Path to the yui folder in the Web root. Path should end
 *                      in a trailing slash (e.g. http://www.example.com/yui/).
 */
STORE.yui.createLoader = function(path)
{
	var loader = new YAHOO.util.YUILoader({
		// Load from the local server so that we can use SSL.
		base : path,
		// We're not running a combo service on the local server.
		combine: false,
		// Ensure we're only ownloading what we absolutely need.
		loadOptional: false,
		timeout: 10000,
		// Control where the elements get inserted so that any styles can be
		// overridden.
		insertBefore: "yui-placeholder"	
	});
	
	// Create a custom module which pulls in the base CSS for panels and the
	// JavaScript libraries that we may need.
	
	loader.addModule({
		name: "store-panel",
		type: "css",
		fullpath: path + "container/assets/container.css",
		requires: ["container", "animation", "connection"]
	});
	
	// Same for tabs.
	
	loader.addModule({
		name: "store-tabs",
		type: "css", 
		fullpath: path + "tabview/assets/border_tabs.css",
		requires: ["tabview"]
	});
	
	// And menus.
	
	loader.addModule({
		name: "store-menu",
		type: "css", 
		fullpath: path + "menu/assets/menu.css",
		requires: ["menu"]
	});
	
	return loader;
};
