// Ecommerce Functions
function updateShipping(url) {
	if(did("service")) {
		url += "/" + did("service").value;
	}
	if(did("carrier")) {
		url += "/" + did("carrier").value;
	}
	async_rpc(url, updateCheckoutModifiers, null);
}

function doUpdateShipping(obj) {	
	if(obj.shipping_method == 'order') {
		did("shipping_total").innerHTML = "$" + obj.shipping;
		did("grand_total").innerHTML = "$" + obj.grand_total;
		showID("grand_total_row");
	}
}

function updateCheckoutModifiers(obj) {
	var before_shipping = false;
	var tbody = did("checkout_table").tBodies[0];
	hideID("shipping_row");
	var sub_total_row_found = false;
	for(var i=0; i < tbody.childNodes.length; i++) {
		if(tbody.childNodes[i].nodeName == 'TR') {
			if(tbody.childNodes[i].id == 'grand_total_row') {
				break;
			} else if(tbody.childNodes[i].id == 'sub_total_row') {
				sub_total_row_found = true;
			} else if(sub_total_row_found) {
				if(tbody.childNodes[i].id != 'shipping_row') {
					tbody.childNodes[i].parentNode.removeChild(tbody.childNodes[i]);
					i--;
				}
			}		
		}	
	}
	
	for ( var i=0; i < tbody.childNodes.length; i++ ) {
		if ( tbody.childNodes[i].nodeName == 'TR' ) {
			if ( tbody.childNodes[i].id == 'billing_first_product' ) {
				break;
			} else if ( tbody.childNodes[i].className == 'discount' ) {
				tbody.childNodes[i].parentNode.removeChild(tbody.childNodes[i]);
				i--;
			}
		}	
	}
	
	var insert_before_target = did("shipping_row");
	
	for(var i=0; i < obj.modifiers.length; i++) {
		if(obj.modifiers[i].service) {
			if(did("carrier")) {
				while(did("carrier").options.length > 0) {
					did("carrier").remove(0);
				}
				
				for(var k=0; k < obj.modifiers[i].carriers.length; k++) {
					var optn = document.createElement("option");
					optn.text = obj.modifiers[i].carriers[k].text;
					optn.value = obj.modifiers[i].carriers[k].value;
					did("carrier").options.add(optn);
				}
				
				did("carrier").value = obj.modifiers[i].carrier;
			}
			
			if(did("service")) {
				while(did("service").options.length > 0) {
					did("service").remove(0);
				}
				
				for(var k=0; k < obj.modifiers[i].services.length; k++) {
					var optn = document.createElement("option");
					optn.text = obj.modifiers[i].services[k].text;
					optn.value = obj.modifiers[i].services[k].value;
					did("service").options.add(optn);
				}
				
				did("service").value = obj.modifiers[i].service;
			}
			
			did("shipping_total").innerHTML = obj.modifiers[i].amount;
			insert_before_target = did("grand_total_row");	
		} else {
			var tr = document.createElement('tr');
			tr.className = 'discount';
			
			var td = document.createElement('td');
			td.className = 'title';
			td.colSpan = '2';
			td.appendChild(document.createTextNode(obj.modifiers[i].name));
			tr.appendChild(td);
			
			var td = document.createElement('td');
			td.className = 'price ' + (obj.modifiers[i].positive ? 'green' : 'red');
			td.appendChild(document.createTextNode(obj.modifiers[i].amount));
			tr.appendChild(td);
			
			tbody.insertBefore(tr, insert_before_target);
		}
	}
	
	hideID('billing_first_product');
	if ( obj.line_modifiers ) {
		for ( var i=0; i < obj.line_modifiers.length; i++ ) {
			var tr = document.createElement('tr');
			tr.className = 'discount';
			
			var td = document.createElement('td');
			td.className = 'title';
			td.colSpan = '2';
			td.appendChild(document.createTextNode(obj.line_modifiers[i].name));
			tr.appendChild(td);
			
			var td = document.createElement('td');
			td.className = 'price ' + (obj.line_modifiers[i].positive ? 'green' : 'red');
			td.appendChild(document.createTextNode(obj.line_modifiers[i].amount));
			tr.appendChild(td);
			
			tbody.insertBefore(tr, did('billing_first_product'));
		}
		showID('billing_first_product');
		did('billing_first_subtotal').innerHTML = '$' + obj.line_total;
	}
	
	showID("shipping_row");
	
	if(obj.promo_error) {
		did('promo_code_error').innerHTML = obj.promo_error;
		showID('promo_code_error');
	} else {
		did('promo_code_error').innerHTML = 'fds';
		hideID('promo_code_error');
	}
	
	if ( obj.giftcard_error ) {
		did('giftcard_error').innerHTML = obj.giftcard_error;
		showID('giftcard_error');
	} else {
		did('giftcard_error').innerHTML = '';
		hideID('giftcard_error');
	}
	
	did('sub_total_row').childNodes[3].innerHTML = '$' + obj.sub_total;
	did("grand_total").innerHTML = "$" + obj.grand_total;
}


function hover(obj, state){
	if(!obj){ return; }
	state = Boolean(state);
	if(state && !hasClass(obj,'hover')){
		addClass(obj,'hover');
	} else if(!state) {
		removeClass(obj,'hover');
	}
}

/**
 * rgbToHex - Converts an rgb color value into a hexidecimal value (without the leading '#')
 * @param	integer		r		Value of the color red in range 0-255;
 * @param	integer		g		Value of the color green in range 0-255;
 * @param	integer		b		Value of the color blue in range 0-255;
 * @return	string				Hexidecimal representation of the the color
 */
function rgbToHex(r,g,b){
	var rgb = slice(arguments,0,3), hex = '0123456789ABCDEF';
	for(var i=0;i < 3;i++){
		rgb[i] = isNaN(parseInt(rgb[i],10)) ? 0 : parseInt(rgb[i],10);
		rgb[i] = Math.round(Math.max(Math.min(255,rgb[i]),0));
		rgb[i] = hex.charAt( (rgb[i]-rgb[i]%16)/16 ) + hex.charAt( rgb[i]%16 );
	}
	return rgb.join('');
}

/**
 * getStyle - Retrieve the value of the current style property
 * @param	mixed		obj		Element id or object
 * @param	string		prop	Name of style property to retrieve
 * @return	mixed				Value of the style property. *Note: Unit values will be returned as integers. (ie. 'px' will be stripped off)*
 */
function getStyle(obj,prop){
	obj = (typeof obj == 'object') ? obj : did(obj);
	if(!obj || !prop){ return null; }
	var regRGB = /rgb\((\d+),\s(\d+),\s(\d+)\)/i, color;
	var compVal = obj.currentStyle ? obj.currentStyle[prop] : window.getComputedStyle(obj,null).getPropertyValue(prop);
	if((color = regRGB.exec(compVal)) != null){ color.shift(); compVal = '#'+rgbToHex.apply(null,color); }
	return isNaN(parseFloat(compVal)) ? compVal : parseFloat(compVal);
}

/**
 * getStyles - Retrieve the current value of several styles
 * @param	mixed	obj		Element id or object
 * @param	string	prop	Name of style property to retrieve (pass another parameter for each style property)
 * @return	object			Object whose keys are the style property and values are the style property's value
 */
function getStyles(obj, prop) {
	var args = slice(arguments,1);
	var styles = {};
	args.forEach(function(arg){
		this[arg] = getStyle(obj,arg);
	},styles);
	return styles;
}

/**
 * DOM Utilities
 */
/**
 * did - Shorcut for getElementById
 * @param	id		The element id you are looking for
 * @param	parent	optional;The parent item to search within
 * @return			Element with matching id
 */
function did(id, parent) {
	return (parent || document).getElementById(id);
}
/**
 * dbn - Shorcut for getElementsByName
 * @param	name	The element name you are looking for
 * @param	parent	optional;The parent item to search within
 * @return			Collection of elements
 */
function dbn(name, parent) {
	return (parent || document).getElementsByName(name);
}
/**
 * dbt - Shorcut for getElementsByTagName
 * @param	tag		The element tagName you are looking for
 * @param	parent	optional;The parent item to search within
 * @return 			Collection of elements
 */
function dbt(tag, parent) {
	return (parent || document).getElementsByTagName(tag);
}
/**
 * dbc - Shortcut for getElementsByClassName
 * @param	className	The className you are looking for
 * @param	parent		optional;The parent item to search within
 * @return				Array of elements
 */
function dbc(className, parent) {
	//use default provided by browser if it exists otherwise use our implementation
	if(document.getElementsByClassName) {
		return (parent || document).getElementsByClassName(className);
	}
	var nodes = slice(dbt('*', parent)), elms = [];
	nodes.forEach(function(node){
		if(hasClass(node, className)) {
			elms.push(node);
		}
	});
	return elms;
}
/**
 * next - Returns the next non-whitespace sibling element
 * @param	el	object	Element node from which to start
 * @return		object	Next non-whitespace child element
 */
function next(el) {
	if(!el || !el.nextSibling) { return null; }
	el=el.nextSibling;
	return (el.nodeType==1) ? el : next(el);
}
/**
 * prev - Returns the previous non-whitespace sibling element
 * @param	el	object	Element node from which to start
 * @return		object	Previous non-whitespace child element
 */
function prev(el) {
	if(!el || !el.previousSibling) { return null; }
	el=el.previousSibling;
	return (el.nodeType==1) ? el : prev(el);
}
/**
 * first - Returns the first non-whitespace child element
 * @param	el	object	Parent element node
 * @return		object	First non-whitespace child element
 */
function first(el) {
	if(!el || !el.firstChild) { return null; }
	el=el.firstChild;
	return (el.nodeType==1) ? el : next(el);
}
/**
 * last - Returns the last non-whitespace child element
 * @param	el	object	Parent element node
 * @return		object	Last non-whitespace child element
 */
function last(el) {
	if(!el || !el.lastChild) { return null; }
	el=el.lastChild;
	return (el.nodeType==1) ? el : prev(el);
}
/**
 * owner - Returns the parent of the element
 * @param	el	object	Element from which to retrieve the parent
 * @return		object	Parent node of the element
 */
function owner(el){
	if(!el || !el.parentNode){ return null; }
	el = el.parentNode;
	return (el.nodeType == 1) ? el : owner(el);
}
/**
 * contains - Returns whether the given node is contained with the element
 * @param el object Element in which to check
 * @param node object Element to check for
 * @return boolean True if node is a child of el, false if not
 */
function contains(el, node){
  return el.contains ?
    el != node && el.contains(node) :
    !!(el.compareDocumentPosition(node) & 16);
}
/**
 * createElement - Shortcut for document.createElement
 * @param	tag		Tag of the element to create
 * @return			Element or null if no tag specified
 */
function createElement(tag) {
	if (!tag) { return null; }
	return document.createElement(tag);
}
/**
 * createTextNode - Shortcut for document.createTextNode
 * @param	text	Text to place inside the new node
 * @return			New text node
 */
function createTextNode(text) {
	return document.createTextNode(String(text));
}
/**
 * hasClass - Returns true if the element has the given class applied
 * @param	obj			Element on which to check for the class
 * @param	className	Class to look for on the element
 * @return				True (if element has the class) / False (if it doesn't)
 */
function hasClass(obj, className) {
	if(!obj || className.trim() == ''){ return false; }
	return (String(obj.className).split(' ').indexOf(className) != -1);
}
/**
 * addClass - Adds a class to an element
 * @param	obj			Element on which to add the class
 * @param	className	Class to add to the element
 * @return				True (if successfully added class) / False (if invalid object or empty classname given)
 */
function addClass(obj, className) {
	if(!obj || className.trim() == '' || hasClass(obj, className)){ return false; }
	obj.className = String(obj.className).split(' ').concat([className]).join(' ');
	return true;
}
/**
 * removeClass - Removes a class from an element
 * @param	obj			Element from which to remove the class
 * @param	className	Class to remove from the element
 * @return				True (if successfully removed class) / False (if invalid object or empty classname given)
 */
function removeClass(obj, className) {
	if(!obj || className.trim() == ''){ return false; }
	obj.className = String(obj.className).split(' ').filter(function(cls){
		return (cls != className);
	}).join(' ');
	return true;
}
/**
 * getDocSize - Get the size of the current viewable document area
 * @return	object		The width, height of the current viewable document area in a keyed object ( obj.width obj.height )
 */
function getDocSize() {
  var w = 0, h = 0;
  if(typeof(window.innerWidth) == 'number'){
    //Non-IE
    w = window.innerWidth;
    h = window.innerHeight;
    var sbar = getScrollbarWidth();
	if(window.scrollMaxY > 0){ w -= sbar['right']; }
	if(window.scrollMaxX > 0){ h -= sbar['bottom']; }
  }else if(document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)){
    //IE 6+ in 'standards mode'
    w = document.documentElement.clientWidth;
    h = document.documentElement.clientHeight;
  }else if(document.body && (document.body.clientWidth || document.body.clientHeight)){
    //IE 4 compatible
    w = document.body.clientWidth;
    h = document.body.clientHeight;
  }
  return {'width':w,'height':h};
}

/**
 * getMaxDocSize - Get the total document size
 * @return	object		The width, height of the total document size in a keyed object ( obj.width obj.height )
 */
function getMaxDocSize() {
	var w = 0, h = 0;
	if(window.innerHeight && typeof window.scrollMaxY == 'number') {
		w = window.innerWidth + window.scrollMaxX;
		h = window.innerHeight + window.scrollMaxY;
		var sbar = getScrollbarWidth();
		if(window.scrollMaxY > 0){ w -= sbar['right']; }
		if(window.scrollMaxX > 0){ h -= sbar['bottom']; }
	} else if(document.body.scrollHeight > document.body.offsetHeight) {
		w = document.body.scrollWidth;
		h = document.body.scrollHeight;
	} else {
		w = document.body.offsetWidth;
		h = document.body.offsetHeight;
	}
	return {'width':w,'height':h};
}

/**
 * getScrollbarWidth - Get the width of the scrollbars on the right/bottom
 * @return	object		The width of the scrollbars on the right/bottom of the window if present ( obj.right, obj.bottom )
 */
function getScrollbarWidth() {
	var size = {'right': 0, 'bottom': 0};
	if(!document || !document.documentElement) {
		return size;
	}
	var docEl = document.documentElement;
	size['right'] = ((typeof window.innerWidth == 'number') ? window.innerWidth : docEl.offsetWidth) - docEl.clientWidth;
	size['bottom'] = ((typeof window.innerHeight == 'number') ? window.innerHeight : docEl.offsetHeight) - docEl.clientHeight;
	return size;
}

/**
 * getScrollXY - Gets the distance the page has been scrolled vertically and horizontally
 * @return	object		The x,y distance in a keyed object ( obj.x obj.y )
 */
function getScrollXY() {
	var sX = 0, sY = 0;
	if(typeof(window.pageYOffset)=='number'){
		//Non-IE
		sY = window.pageYOffset;
		sX = window.pageXOffset;
	}else if(document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)){
		//IE 6+ in 'standards mode'
		sY = document.documentElement.scrollTop;
		sX = document.documentElement.scrollLeft;
	}else if(document.body && (document.body.scrollLeft || document.body.scrollTop)){
		//IE 6 in 'strict mode' & some other browsers
		sY = document.body.scrollTop;
		sX = document.body.scrollLeft;
	}
	return {'x':sX,'y':sY};
}

/**
 * Array and object functions
 */


/**
 * Array methods indexOf, forEach, map, filter
 * 		Added for browsers without native support
 */
Array.prototype.indexOf = (function() {
	var fn;
	if(typeof Array.prototype.indexOf == 'function') {
		fn = Array.prototype.indexOf; 
	} else {
		fn = function indexOf(obj,start){
			var len = this.length;
			start = Number(start) || 0;
			start = (start < 0) ? Math.ceil(start) : Math.floor(start);
			if(start < 0){ start+= len; }
			for(;start < len;start++){
				if(start in this && this[start]===obj){ return start; }
			}
			return -1;
		}
	}
	var indexOf = null;
	return fn;
})();
Array.prototype.forEach = (function() {
	var fn;
	if(typeof Array.prototype.forEach == 'function') {
		fn = Array.prototype.forEach;
	} else {
		fn = function forEach(fn /*, bind*/){
			var len = this.length;
			if(typeof(fn) != 'function'){ throw new TypeError(); }
			var bind = arguments[1];
			for(var i=0;i < len;i++){
				if(i in this){ fn.call(bind, this[i], i, this); }
			}
		};
	}
	var forEach = null;
	return fn;
})();
Array.prototype.map = (function() {
	var fn;
	if(typeof Array.prototype.map == 'function') {
		fn = Array.prototype.map;
	} else {
		fn = function map(fn /*, bind*/){
			var len = this.length;
			if(typeof(fn) != 'function'){ throw new TypeError(); }
			var ret = [], bind = arguments[1];
			for (var i=0;i < len;i++){
				if(i in this){ ret[i] = fn.call(bind, this[i], i, this); }
			}
			return ret;
		};
	}
	var map = null;
	return fn;
})();
Array.prototype.filter = (function() {
	var fn;
	if(typeof Array.prototype.filter == 'function') {
		fn = Array.prototype.filter;
	} else {
		fn = function filter(fn /*, bind*/){
			var len = this.length;
			if(typeof(fn) != 'function'){ throw new TypeError(); }
			var ret = [], bind = arguments[1], val=null;
			for(var i=0;i < len;i++){
				if(i in this){
					val=this[i];
					if(fn.call(bind,val,i,this)){ ret.push(val); }
				}
			}
			return ret;
		};
	}
	var filter = null;
	return fn;
})();

/**
 * slice - Shortcut for Array.prototype.slice.call(obj, idx)
 * @param	obj		mixed		Object to call array.slice on
 * @param	start	integer		Index at which to begin slicing
 * @param	end		integer		Index at which to end slicing
 * @return			array		New array containing values from the idx to the end of the obj
 *		Note: Useful for transforming arguments object and collections into regular arrays
 *		ex.	function(){ var args = slice(arguments); alert(args instanceof Array); }
 */
function slice(obj, start, end){
	var ret = obj;
	if(window.ActiveXObject){
		if(typeof obj.length=='undefined'){ obj.length = getLength(obj); }
		ret = Array.prototype.map.call(obj,function(item){return item;});
	}
	var args = [(start || 0)];
	if(end && !isNaN(Number(end))) {
		args.push(Number(end));
	}
	return Array.prototype.slice.apply(ret,args);
}

/**
 * getLength - Finds the total number of all non-function properties owned by the object (see hasOwnProperty)
 * @param obj    object    The object whose properties to count.
 * @return       int       The length of the object or 0 if obj was invalid
 */
function getLength(obj){
    if(!obj){ return 0; }
    var i=0;
    for(var key in obj){
        if(obj.hasOwnProperty(key) && typeof obj[key] !='function'){i++;}
    }
    return i;
}

/**
 * DOM Event Functions & Objects
 */

/* domLoader - detects when the DOM is ready in the browser (typically before onload would fire).
 * Also allows registration of functions to execute once ready.
 * 
 * Example using the optional object and args parameters:
 *		-keyword 'this' will be the person object
 * 		-arg1 and arg2 become 'name' and 'job'
 * 		-function will log:
 * 				name: bob job: plummer
 * 		
 * 		var person = {'name':'bob','job':'plummer'};
 * 		domLoader.register(function(arg1, arg2){
 * 			console.log(arg1+': '+this[arg1]+' arg2: '+this[arg2]);
 * 		}, person, ['name','job']); 
 */
var domLoader = {
	isReady: false,
	isBound: false,
	queue: [],
	binds: [],
	args: [],
	/**
	 * register - Register a function to be executed when the dom is ready
	 * @param	fn		function	Function to execute
	 * @param	obj		object		optional;Object that will be the scope for fn(keyword 'this' inside function, default is window)
	 * @param	args	array		optional;Array of parameters to be passed to the function upon execution
	 */
	register: function register(fn, obj, args) {
		this.checkReady();
		this.queue = (this.queue instanceof Array) ? this.queue : [];
		if(!fn || typeof fn != 'function') {
			return;
		}
		
		var offset = this.queue.push(fn) - 1;
		
		obj = obj || null;
		this.binds = (this.binds instanceof Array) ? this.binds : [];
		this.binds[offset] = obj;
		
		args = args ? ((args instanceof Array) ? args :  slice(args)) : [];
		this.args = (this.args instanceof Array) ? this.args : [];
		this.args[offset] = args;
		if(this.isReady) {
			this.ready();
		}
		return this;
	},
	/**
	 * checkReady - Starts checking the for the dom to be ready (internal use)
	 */
	checkReady: function checkReady() {
		if(this.isBound) {
			return;
		}
		this.isBound = true;
		var self = this;
		var events = {'load': window};
		// Mozilla, Opera and Safari
		if(document.addEventListener) {
			events['DOMContentLoaded'] = document;
		// IE
		} else if(document.attachEvent) {
			events['onreadystatechange'] = document;
			// If IE and not an iframe -- continually check to see if the document is ready
			if(document.documentElement.doScroll && window == window.top) {
				(function ieDoScrollTest(){
					if(self.isReady) {
						return;
					}
					try {
						// If IE is used, use the trick by Diego Perini -- http://javascript.nwbox.com/IEContentLoaded/
						document.documentElement.doScroll("left");
						// and execute any waiting functions
						self.isReady = true;
						self.ready();
					} catch(error) {
						setTimeout(arguments.callee, 0);
						return;
					}
				})();
			}
		}
		var idDoScrollTest = null;
		//Faster Safari detection
		if(typeof navigator.taintEnabled === 'undefined') {
			var timer = window.setInterval(function safariReadyTest() {
				if(/loaded|complete/.test(document.readyState)){
					window.clearInterval(timer);
					if(self.isReady) {
						return;
					}
					self.isReady = true;
					self.ready();
				}
			}, 10);
		}
		var safariReadyTest = null;
		for(var name in events) {
			if(!events.hasOwnProperty(name)) {
				continue;
			}
			(function scopeFix(name, obj) {
				registerEvent(obj, name, function pageLoadEventWrapper() {
					unregisterEvent(obj, name, arguments.callee);
					if(!self.isReady) {
						self.isReady = true;
						self.ready();
					}
				});
				var pageLoadEventWrapper = null;
			})(name, events[name]);
		}
		var varprotector = null;
	},
	/**
	 * ready - Executes all registered functions with any bound scope & args (internal use)
	 */
	ready: function ready(){
		if(!this.isReady || !(this.queue instanceof Array)) {
			return;
		}
		var fn, obj, args;
		while(fn = this.queue.shift()) {
			obj = this.binds.shift() || window;
			args = this.args.shift();
			fn.apply(obj, args);
		}
		
		this.queue = [];
		this.binds = [];
		this.args = [];
	}
};

/**
 * triggerEvent - Manually fires an event on an element
 * @param	el			object		Element on which to fire the event
 * @param	type		string		Event type to fire
 * @param	bubbles		boolean		Whether the event bubbles through the DOM (no effect on ie)
 * @param	cancelable	boolean		Whether the event can be cancelled with preventDefault (no effect on ie)
 */
function triggerEvent(el, type, bubbles, cancelable) {
	try{
		el = typeof el == 'object' ? el : did(el);
		bubbles = bubbles || true;
		cancelable = cancelable || true;
		if(document.createEvent){
			var groups = {
				'UIEvents':['focusin','focusout','activate','deactivate'],
				'MouseEvents': ['click','dblclick','mousedown','mouseup','mouseover','mouseout','mousemove'],
				'HTMLEvents': ['load','unload','abort','error','select','change','submit','reset','focus','blur','resize','scroll']
			};
			var groupName = null;
			for(var group in groups) {
				if(groups[group].indexOf(type) != -1) {
					groupName = group;
					break;
				}
			}
			if(groupName) {
				var event = document.createEvent(groupName);
				event.initEvent(type, bubbles, cancelable);
				//safari 3 doesn't have window.dispatchEvent()
				(el == window && !el.dispatchEvent ? document : el).dispatchEvent(event);
				return true;
			}
		}else if(document.createEventObject){
			var event = document.createEventObject();
			// IE6,IE7 thinks window==document and doesn't have window.fireEvent()
			// IE6,IE7 cannot properly call document.fireEvent()
			(el == document ? document.documentElement : el).fireEvent("on"+type, event);
			return true;
		}
	}catch(e){}
	return false;
}
