/* $Rev: 224 $ */

// run this before .ready()
getBiz();

// INIT: any global methods that are initialized always
jQuery(document).ready(function()
{
	initPchData(); 
});

// hydrate data elements if passed from php
function initPchData()
{
	if (window.pchData == undefined)
		return;

	delim = '|||'; // match delim in js_handoff php method
	for(k in window.pchData)
	{
		this_var = window.pchData[k];
		keydata = k.split(delim);

		if (keydata.length == 2) // had to find the split
		{
			this_elem = jQuery(keydata[0]);

			if (this_elem.length > 0)
				this_elem.data(keydata[1], this_var);
		}
	}
}

/*
 *	what's the biz?
 *	for now, we don't know when the outlet is present, since we use PCH for styling.
 *	@todo add outlet support where needed
 */
function getBiz()
{
	var bizlist = ['outlet', 'pch', 'dash', 'fresh']; // outlet first, since we're tagging body with both pch and outlet classes
	var body = jQuery('body');
	var biz = null;

	jQuery.each(bizlist, function(k, v)
	{
		if (body.hasClass(v))
		{
			biz = v;
			return false; // break from the loop, but will window.biz still be set?
		}
		return true; // continue
	});

	window.biz = biz;
}

// pass name, name_popup and name_link are assumed as id's.
function helpbox(name, o)
{
	var popname = '#' + name + '_bubble';
	var linkname = '#' + name + '_link';
	var help_link_id = name + '_anchor';

	o = o || {};

	var def_options = {
		position: 'anchor'
	, 	anchor: 'cover'
	, 	timeout: 8000
	, 	width: 400
	,	minHeight: 50
	,	mouseoff: false
	,	dialogClass: ' dialog-help '
	};

	var options = jQuery.extend({}, def_options, o);

	// for backwards compatibility, allow lookup by class if no id exists
	var _link = jQuery(linkname);

	if (_link.length == 0)
		_link = jQuery(linkname.replace('#', '.'));


	// if we still don't find a link using class, there's no link. and with no link, no popup
	if (_link.length == 0)
		return false;

	var anchor_html = '<a href="#" class="help ' + help_link_id + '"></a>';

	if (_link.get(0).tagName == 'IMG')
		_link.wrap(anchor_html);
	else _link.wrapInner(anchor_html);

	// need to move helpbox contents to another container since it's default container is hidden;
	var popup = jQuery(popname).addClass('help_bubble');
	popup.pchDialog('a.' + help_link_id, options);

	link = jQuery('a.' + help_link_id);
	link
	.hoverIntent
	(
		function()
		{
			jQuery(this).click();
		}
	, 	function(){}
	)
	.click
	(
		function()
		{
			jQuery('div.ui-dialog.dialog-help div.ui-dialog-content')
			.hover
			(
				function(){}
			,	function()
				{
					jQuery(this).dialog('close');
				}
			);
		}
	);

	if (options.mouseoff)
		popup.mouseout(function(){jQuery(this).dialog('close');});

	return true;
}

function is_pch()
{
	return jQuery('body').hasClass('pch');
}

function is_dash()
{
	return jQuery('body').hasClass('dash');
}

jQuery.fn.pchDialog = function(trigger, o)
{
	var popup = this;

	if (popup.length == 0)
		return this;

	var trigger_ele = jQuery(trigger).addClass('uiTrigger');

	o = o || {};

	var def_options = {
		bgiframe: true
	,	autoOpen: false
	,	trigger_event: 'click'
	,	show_title: false
	,	dialogClass: ''
	,	resizable: false
	,	draggable: false
	,	open: function()
		{
			// don't hide existing popup if that popup initiated this one
			var is_in_dialog = trigger_ele.parents('div.ui-dialog').length > 0;

			if ( ! is_in_dialog)
			{
				jQuery('div.ui-dialog .ui-dialog-content:visible').not(this).dialog('close');
			}

			return true;
		}
	,	timeout: false
	,	anchor: false
	,	anchor_element: false
	,	triggerToggle: false
	,	offset: {}
	,	pch_open: function(){}
	,	pch_init: function(){}
	,	persist: false
	};

	var options = jQuery.extend({}, def_options, o);

	if (options.show_title === false)
		options.dialogClass += ' no-titlebar ';

	if (options.triggerToggle === true)
		trigger_ele.addClass('triggerToggle');

	if (options.persist === true)
		options.dialogClass += ' persist ';

	// initialiaze the dialog
	popup.dialog(options);

	// call initialize callback
	options.pch_init(trigger, popup);

	trigger_ele
	.bind(options.trigger_event, function(event)
	{

		if (popup.dialog('isOpen'))
		{
			if (trigger_ele.hasClass('triggerToggle'))
				popup.dialog('close');

			return false;
		}
		else
		{
			event.preventDefault();
			event.stopPropagation();
			event.stopImmediatePropagation();
		}

		// determine coordinates to pass to dialog based on anchor param
		if (options.anchor !== false)
		{
			var new_position = getDialogPosition(popup, jQuery(this), options.anchor, options);
			popup.dialog('option', 'position', new_position);
		}

		var target = jQuery(event.target);
		var open_status = options.pch_open(target, popup);

		if (open_status === false)
			return false;

		// show current
		popup.dialog('open');

		if (options.timeout !== false)
			setTimeout("jQuery('#" + popup.attr('id') + "').dialog('close');", options.timeout);

		//if for some reason the popup doesn't work, allow link to pass
		return ! popup.dialog('isOpen');
	});

	// if a.close_button is present, bind to close popup
	var close_button = popup.closest('div.ui-dialog').find('.ui-dialog-title a.bubble_close');
	close_button
	.click
	(
		function(e)
		{
			popup.dialog("close");
		}
	);

	return this;
};

// general dialog close event, if mouse clicked anywhere except an open dialog or it's trigger, close the open dialog
jQuery(document).mouseup(function(event){
	// if no open popups, nothing to worry about
	var allPopups = jQuery('div.ui-dialog:not(.persist) .ui-dialog-content:visible');
	if (allPopups.length < 1)
		return;

	var ele = jQuery(event.target);

	var notDialog = ele.closest('div.ui-dialog').length == 0;
	var notUI = ele.closest('div.ui-widget-overlay').length == 0 && ele.closest('div.ui-widget').length == 0;
	var notTrigger = ele.closest('.uiTrigger').length == 0;

	// shouldn't close this if it has an overlay (there's a close button for that)
	var has_overlay = jQuery('div.ui-widget-overlay').length > 0;

	// and for clicking on the scrollbar or other non-viewpane stuff
	var is_html_container = ele.is('html');

	// if dialog creates an impromptu popup, don't close it
	var is_prompt = ele.closest('div.jqi, div.product_eta_change, div.jqifade, div.product_eta_changefade').length > 0;

	// if the click is off the dialog, and not clicking the trigger, close any open dialogs
	if (notDialog && notUI && notTrigger && ! has_overlay && ! is_html_container && ! is_prompt)
		allPopups.each(function(){jQuery(this).dialog('close');});
});



function getDialogPosition(dialog, trigger_element, anchor, options)
{
	var top = false, left = false;

	var trigger_ele = options.anchor_element !== false
		? options.anchor_element
		: trigger_element;

	var offset = trigger_ele.offset();

	top = offset.top + trigger_ele.height();

	var dialog_size = false, dialog_width = false, dialog_height = false;
	var wnd = jQuery(window);

	switch(anchor)
	{
		case 'fixed':
			left = offset.left;
			top = offset.top;
		break;

		case 'left': // popup and anchor's left edge line up
			left = offset.left;
		break;

		case 'left-top': // popup tr meets trigger tl
			dialog_size = get_element_size(dialog, options);
			dialog_width = dialog_size.width;

			left = offset.left - dialog_width;
			top = offset.top;
		break;

		case 'left-center':
			dialog_size = get_element_size(dialog, options);
			dialog_width = dialog_size.width;
			dialog_height = dialog_size.height;

			left = offset.left - dialog_width;
			top = offset.top - dialog_height/2 + trigger_ele.height()/2;
		break;

		case 'left-quarter':
			dialog_size = get_element_size(dialog, options);
			dialog_width = dialog_size.width;
			dialog_height = dialog_size.height;

//			console.log("dialog_height: " + dialog_height);


			left = offset.left - dialog_width;
			top = offset.top - dialog_height/4 + trigger_ele.height()/2;
		break;

		case 'right': // popup and anchor's right edge line up
			left = offset.left + trigger_ele.width();

			dialog_size = get_element_size(dialog, options);
			dialog_height = dialog_size.height;

			top = (wnd.height() / 2) - (dialog_height / 2);
		break;

		case 'cover': // popup appears centered over anchor

			dialog_size = get_element_size(dialog, options);
			dialog_width = dialog_size.width;
			dialog_height = dialog_size.height;

			left = offset.left - dialog_width/2 + trigger_ele.width()/2;
			top = offset.top - dialog_height/2 + trigger_ele.height()/2;
		break;

		case 'above':
			dialog_size = get_element_size(dialog, options);
			dialog_width = dialog_size.width;
			dialog_height = dialog_size.height;

			left = offset.left - dialog_width/2 + trigger_ele.width()/2;
			top = offset.top - dialog_height;
		break;

		case 'tr': // popup top-right edge meets anchor's bottom-left edge

			dialog_size = get_element_size(dialog, options);
			dialog_width = dialog_size.width;

			left = offset.left - dialog_width;
		break;

		case 'tr-tl':
			dialog_size = get_element_size(dialog, options);
			dialog_width = dialog_size.width;

			left = offset.left - dialog_width;
			top = offset.top;
		break;

		case'listproduct_ordering': // special for dash listproduct ordering popout
			top = offset.top - 20; // account for popup header
			left = offset.left - 5; // so rounded corner doesn't line up exactly with corner of image
		break;

		case 'subcat':
			left = offset.left - 2;
			top = top - 2;
		break;

		case 'listorder_notice':
			var table = jQuery('#listorder_tabs');
			offset = table.offset();

			top = offset.top + 67 ;
			left = offset.left + 60;
		break;

		case 'container_notice':
			var table = jQuery('#container_tabs');
			offset = table.offset();

			top = offset.top + 67 ;
			left = offset.left + 60;
		break;

		case 'center': // popup is centered just below anchor
		default:

			dialog_size = get_element_size(dialog, options);
			dialog_width = dialog_size.width;

			left = offset.left + (trigger_ele.width() / 2) - (dialog_width / 2);
		break;
	}

	// also need to take into account the viewport positioning, account for IE being a fucking retard
	var yPage = window.pageYOffset || document.documentElement.scrollTop || 0;
	var xPage = window.pageXOffset || document.documentElement.scrollLeft || 0;

	// get viewport dimensions
	// the more standards compliant browsers (mozilla/netscape/opera/IE7) use window.innerWidth and window.innerHeight

	 if (typeof window.innerWidth != 'undefined')
	 {
		  viewportwidth = window.innerWidth,
		  viewportheight = window.innerHeight
	 }
	 else if (typeof document.documentElement != 'undefined'
		 && typeof document.documentElement.clientWidth !=
		 'undefined' && document.documentElement.clientWidth != 0) // IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document)
	 {
		   viewportwidth = document.documentElement.clientWidth,
		   viewportheight = document.documentElement.clientHeight
	 }
	 else // older versions of IE
	 {
		   viewportwidth = document.getElementsByTagName('body')[0].clientWidth,
		   viewportheight = document.getElementsByTagName('body')[0].clientHeight
	 }

	// account for viewport position, apparently the new dialog goes from there...
	left = left - xPage;
	top = top - yPage;

	// don't let dialog go off left side of screen
	if (left < 0)
		left = 0;

	// don't let dialog go off top of screen
	if (top < 0)
		top = 0;

	if (dialog_width !== false)
	{
		if (left + dialog_width > xPage + viewportwidth)
			left = (xPage + viewportwidth) - dialog_width;
	}

	var offset_left = options.offset.left || 0;
	var offset_top = options.offset.top || 0;

	left = left + offset_left;
	top = top + offset_top;

	return [left, top];
}

function get_element_size(element, o)
{
	var options = o || {};

	var container = element.closest('div.ui-dialog').length > 0
		? element.closest('div.ui-dialog')
		: element;

	var position = container.css('position');
	var top = container.css('top');
	var display = container.css('display');

	// a browser can't properly calculate dimensions unless it's visible
	// so, we'll move it out of the viewport, show it, calculate dimensions, then put it back
	container.css('position', 'relative').css('top', 10000).css('display', 'block');

	var element_width = options.width || element.width() || element.css('width');
	var element_height = options.height || element.height() || element.css('height') || options.minHeight;

	container.css('display', display).css('position', position).css('top', top);

	if (element_width == 'auto')
		element_width = 0;
	if (element_height == 'auto')
		element_height = 0;

	// we have to use .css() for some dimensions, since the dialog is still hidden when we're determining position
	// this can cause problems because the value will have 'px' or 'em' or 'ex', or at the worst case a percentage...
	// lets do the best we can with this, if it's in pixels, parseInt, if em or ex, parseInt*fontsize conversion? percentages...whatever
	element_widthFloat = parseFloat(element_width);
	if (element_width != element_widthFloat)
	{
		element_widthInt = parseInt(element_width);
		if (element_width.indexOf('%') != -1)
			element_width = dialog.parent().width() * element_widthInt / 100; // take percentage of parent
		else if (element_width.indexOf('ex') != -1)
			element_width = element_widthInt*6; // at 100%, 1ex ~= 6px
		else if (element_width.indexOf('em') != -1)
			element_width = element_widthInt*12; // at 100%, 1em ~= 12px
		else
			element_width = parseInt(element_width); // either in px, or something else. if we get to this, we'll probably just adjust the css to be usable
	}

	// do the same processing for element_height value as element_width
	element_heightFloat = parseFloat(element_height);
	if (element_height != element_heightFloat)
	{
		element_heightInt = parseInt(element_height);
		if (element_height.indexOf('%') != -1)
			element_height = dialog.parent().height() * element_heightInt / 100; // take percentage of parent
		else if (element_height.indexOf('ex') != -1)
			element_height = element_heightInt*6; // at 100%, 1ex ~= 6px
		else if (element_height.indexOf('em') != -1)
			element_height = element_heightInt*12; // at 100%, 1em ~= 12px
		else
			element_height = parseInt(element_height); // either in px, or something else. if we get to this, we'll probably just adjust the css to be usable
	}

	// if for some reason that processing didn't work, just set them to 0...
	if (isNaN(element_width))
		element_width = 0;
	if (isNaN(element_height))
		element_height = 0;

	var element_size = {width: element_width, height: element_height};

	return element_size;
}

/**
	like jQuery.post() but with a few methods for inserting returned content
 */
jQuery.pchPost = function(action, values)
{
	values += '&ajax=true';

	jQuery.post(
		action
	,	values
	,	function(data)
		{
			//returned info should either be 'failed' or <div.message><div.content>
			if (data != 'failed')
			{
				//won't be recognized as traversable html otherwise
				var retval = jQuery('<div id="container">' + data + '</div>');

				var message = jQuery('div.message', retval).html();

				//content needs to be set up so jquery understands it(e.g. if it's a tr, needs to be wrapped in a <table><tbody></tbody></table>
				var content = jQuery('.content', retval).children();

				// message always contained in div#message
				if (message)
				{
					jQuery('#message').html(jQuery(message)); // should there be a timeout which clears the message div?

					setTimeout("jQuery('#message').slideUp().html('').show();", 30000);
				}

				if (content.length > 0)
				{
					content.each(
						function()
						{
							var $this = jQuery(this);
							var thisid = $this.attr('id');
							var thiscontent = $this.html();

							jQuery('#' + thisid).html(thiscontent);
						});
				}

				dsrInit();
			}
		}
	,	"html"
	);
}

/**
	takes input objects, and jumps through them as though they were 1 input
 */
jQuery.pchInputLink = function(inputList)
{
	var max = inputList.length - 1;

	jQuery.each(
		inputList
	, 	function(i, v)
		{
			var input = inputList[i];
			var prev = (i > 0) ? inputList[i-1] : false;
			var next = (i < max) ? inputList[i+1] : false;

			input.keyup(
				function(e)
				{
					var $this = jQuery(this);
					var keyCode = e.keyCode;

					// only jump through boxes if keyCode in whitelist
					// 0-9(48-57), numpad 0-9(96-105), aA-zZ(65-90), bkspc(8)
					if ((keyCode > 57 && keyCode < 65) || (keyCode > 90 && keyCode < 96) || keyCode > 105 || (keyCode < 48 && keyCode != 8))
						return;

					//37 = left arrow, 39 = right, incorporate these at some point

					if ($this.val().length == $this.attr('maxlength') && next !== false) // forward to next box
					{
						if (next.val().length > 0)
							next.focus().select();
						else next.focus();
					}
					else if ((keyCode == '8' || keyCode == '46') && $this.val().length < 1 && prev !== false) // go to prev box
					{
						if (prev.val().length > 0)
							prev.focus().val(prev.val());
						else prev.focus();
					}
				});
		}
	);
}

/**
 * used on date object, will return given date as a string in a particular format MM/dd/yy
 */
jQuery.pchDate = function(d)
{
	var month = (d.getMonth() + 1).toString();
	if( month.length == 1 )
		month = '0' + month;

	var day = d.getDate().toString();
	if( day.length == 1 )
		day = '0' + day;

	var year = d.getYear().toString().substring(1);

	var dateString = month + '/' + day + '/' + year;

	return dateString;
}

jQuery.fn.defaultText = function(defText, o)
{
	var def_options = {
		persist: false
	};

	var options = jQuery.extend({}, def_options, o);

	var inputs = this;

	// in case there's no input
	if (inputs.length == 0)
		return false;

	inputs.each
	(
		function()
		{
			var input = jQuery(this);

			var maxLen = parseInt(input.attr('maxlength'));
			if (isNaN(maxLen) || maxLen < 1)
				maxLen = 40;

			input
			.attr('maxlength', defText.length)
			.focus(
				function()
				{
					input.addClass('focus');
					if (input.hasClass('default_text_display') && options.persist)
						input.get(0).setSelectionRange(0, 0);
				}
			).mousedown(
				function(e)
				{

					if (input.hasClass('default_text_display') && options.persist)
					{
						e.preventDefault();
						input.get(0).setSelectionRange(0, 0);
						return false;
					}

				}
			).mouseup(
				function()
				{
					if (input.hasClass('default_text_display') && options.persist)
						input.get(0).setSelectionRange(0, 0);
				}
			).keydown(
				function(e)
				{
					if(input.val() == defText && (e.which != 8 || ! options.persist)) // if displaying default text, cursor is at beginning
					{
						input.attr('maxlength', maxLen).val('').removeClass('default_text_display').removeClass('focus');
					}
					else if (input.val() == '' && options.persist)
					{
						input.attr('maxlength', defText.length).val(defText).addClass('default_text_display').addClass('focus');
						input.get(0).setSelectionRange(0, 0);
					}
				}
			).blur(
				function()
				{
					input.removeClass('focus');
					if (input.val() == '')
					{
						input.attr('maxlength', defText.length).val(defText).addClass('default_text_display');
					}
				}
			);

			// if input is currently empty, show the default text and enable styling
			if (input.val().length == 0)
				input.val(defText).addClass('default_text_display');

			// avoid sending default text on submit, might cause errors
			jQuery('input[type=submit]').mousedown(
				function()
				{
					if (input.val() == defText)
					{
						input.val('').removeClass('default_text_display');
					}
				});
		}
	);

	return this;
};

/**
	Disables submit button(s)

	condition 	string 	button disabled if eval'd to true
	o			object	options
						callback	 (function to call when closing message popup)
						noticeText	 (explanation for disabled button, accepts html)
						watchTrigger (DOM elements that will trigger condition check)
						triggerEvent (array of events that will trigger condition check, default is 'change')
						list_incomplete (list all inputs with 'incomplete' class)
	po			object	prompt popup options
						see jquery.impromptu.js
 */
jQuery.fn.disableButton = function(condition, o, po)
{
	var defOptions = {
		callback: false
	,	noticeText: false
	,	watchTrigger: false
	,	triggerEvent: ['change']
	,	list_incomplete: false
	,	show_prompt: true
	};

	var options = jQuery.extend({}, defOptions, o);

	var defpo = {
		overlayspeed: 0.01
	,	promptspeed: 0.01
	,	show: 'fadeIn'
	,	submit: function()
		{
			if (jQuery.isFunction(options.callback))
				options.callback();

			return true;
		}
	};

	var oPrompt = jQuery.extend({}, defpo, po);

	var button = this;
	var safari = false; //jQuery.browser.safari;

	button.mousedown(
		function(event)
		{
			//console.log(condition + ': "' + eval(condition) + '"');
			if (event.which != 1 && event.which != undefined)
				return true;

			var this_button = jQuery(this);

			if (jQuery('div.jqi').length > 0)
				return false;

			var disable_button = eval(condition);
			if (disable_button)
			{
				event.preventDefault();

				// we're adding class 'incomplete_highlight' to any required fields that haven't been filled out
				var incomplete_labels = '';
				if (options.list_incomplete !== false)
				{
					var incomplete_fields = jQuery('input.incomplete, select.incomplete, textarea.incomplete, input.valid_me[valid!=0]:not(.pre-valid)');
					incomplete_fields.each(
						function()
						{
							var this_input = jQuery(this);
							var input_id = this_input.attr('id');

							var label = jQuery('label[for=' + input_id + ']');

							if (label.length == 0)
								return;

							var label_text = label.text().replace('*', '').replace(':', '');

							if (incomplete_labels.length > 0)
								incomplete_labels += '<br />';
							incomplete_labels += label_text;
						}
					)
				}

				if (options.noticeText.length > 0)
				{
					var message = options.noticeText;
					if (incomplete_labels.length > 0)
					{
						incomplete_labels = '<div class="incomplete_labels">' + incomplete_labels + '</div>'
						message += '<br /><br />The following fields need attention:<br />' + incomplete_labels;
					}

					if (options.show_prompt)
					{
						hide_ve_messages();
						jQuery.prompt(message, oPrompt);
					}
					else button.closest('form').validationEngine('validate');
				}

				return false;
			}
			else
			{
				if (safari)
					button.attr('disabled', false);

				jQuery('input.default_text_display').val('');
				setTimeout("jQuery('input.default_text_display').keyup()", 2000);

				this_button.removeClass('disabled_button');

				// actually follow through with click and shit
				if (jQuery.browser.msie || jQuery.browser.mozilla)
				{
					this_button.closest('form').unbind();
					this_button.unbind().click();
				}
				return true;
			}
		}
	).click(
		function()
		{
			if (eval(condition))
				return false;

			jQuery(this).closest('form').unbind();

			jQuery(this).unbind().trigger('click');

			// disable the button for 1 second to prevent double clicks
			this_button = jQuery(this);
			if (typeof this_button.attr('id') !== 'undefined' && this_button.attr('id').length > 0)
			{
				this_button.attr('disabled', true);
				setTimeout("jQuery('#" + this_button.attr('id') + "').attr('disabled', false);", 1000);
			}
			
			return false;
		}
	).closest('form').submit(
		function()
		{
			var disable_form = eval(condition);

			if (disable_form)
				return false;
			else
			{
				jQuery(this).unbind().submit().validationEngine('hide');
				return false;
			}
		}
	);


	//check condition, and assign to trigger
	if (eval(condition))
	{
		button.addClass('disabled_button');

		if (safari)
			button.attr('disabled', true);
	}
	//console.log(condition + ': "' + eval(condition) + '"');

	if (options.watchTrigger)
	{
		// force single trigger event to be array
		if (typeof options.triggerEvent != "object")
			options.triggerEvent = [options.triggerEvent];

		// need string key or this overwrites the first entry in the options.triggerEvent list
		var custom_events = {custom: 'force_validate'};
		var triggerEvents = jQuery.extend({}, options.triggerEvent, custom_events);

		jQuery.each(triggerEvents,
			function(i, tEvent)
			{
				jQuery(options.watchTrigger).bind(tEvent,
					function(e)
					{

						// console.log(condition + ': "' + eval(condition) + '"');
						
						var disable_button = eval(condition);

						if (disable_button)
						{
							button.addClass('disabled_button');

							if (safari)
								button.attr('disabled', true);
						} else {
							if (safari)
								button.attr('disabled', false);

							button.removeClass('disabled_button');
						}

						if (tEvent == 'keyup' || tEvent == 'keydown')
						{
							var which = e.which;
							if (which == 13) // enter
								button.closest('form').submit();
						}
					});
			});
	}

	// in case we set disabled_button class in the template, re-evaulate on page load so it doesn't look disabled
	jQuery(function()
	{
		jQuery(options.watchTrigger).trigger(options.triggerEvent[0]);
	});

	return this;
}

jQuery.fn.hoverState = function()
{
	jQuery(this)
	.hover
	(
		function()
		{
			var $this = jQuery(this);
			if ( ! $this.hasClass('.disabled_button'))
				$this.addClass('hover');
		}
		,
		function()
		{
			var $this = jQuery(this);
			if ( ! $this.hasClass('.disabled_button'))
				jQuery(this).removeClass('hover');
		}
	)
	.mousedown(function(){jQuery(this).addClass('active')})
	.mouseup(function(){jQuery(this).removeClass('active')})
	.mouseout(function(){jQuery(this).removeClass('active')})
}

jQuery.fn.hoverClass = function(c, onIntent) {
	var action = onIntent === true ? 'hoverIntent' : 'hover';

    return this.each(function(){
        jQuery(this)[action](
			function() {jQuery(this).addClass(c);}
		,	function() {jQuery(this).removeClass(c);}
        );
    });
};

// allow thumbnails to update one main image
// main - pass main beauty shot object
// thumbs - pass thumbs object, usually links wrapping thumbs
// attr_map - define attributes to copy from thumb to beauty, {main attr: thumb attr, main attr2: thumb attr2 ... }
// options - event to trigger swap, etc.
// TODO: add animation for different widths as well as height
jQuery.fn.beautySwap = function(thumbs, attr_map, o)
{
	var main = this;

	// if no map passed, thumb href becomes beauty src
	attr_map_defaults = {
		"src": "href"
	,	"alt": "alt"
	}

	options_defaults = {
		swapEvent: 'click'
	,	callback: function(){}
	,	hash_prefix: 'pch_'
	,	active_class: 'active'
	}

	attributes = attr_map || attr_map_defaults;
	var options = jQuery.extend({}, options_defaults, o);

	// if no main image or no thumbs, no reason to go on
	if (thumbs.length == 0)
		return false;

	var main_parent = main.parent();
	var main_parent_height = main.height();
	main_parent.attr('size_fix', 0);

	thumbs.bind(options.swapEvent,
		function(e)
		{
			e.preventDefault();
			e.stopPropagation();

			var $thumb = jQuery(this);

			main_parent.addClass('loading');

			if ( main_parent.attr('size_fix') == 0)
			{
				if (main_parent_height != 0)
					main_parent.height(main_parent_height);

				main_parent.attr('size_fix', 1);
			}

			if($thumb.parent().attr('id').length > 0)
				window.location.hash = options.hash_prefix + $thumb.parent().attr('id')

			thumbs.closest('li').removeClass(options.active_class);
			$thumb.closest('li').addClass(options.active_class);

			main.stop().fadeTo
			(
				'fast'
			,	0.01
			,	function()
				{
					var same_image = true;
					jQuery.each
						(
							attributes
						,	function(main_attr, thumb_attr)
							{
								if (main.attr(main_attr) != $thumb.attr(thumb_attr) && same_image === true)
									same_image = false;

								main.attr(main_attr, $thumb.attr(thumb_attr));
							}
						);

					// if all attrs match up, just fade back in
					if (same_image === true)
					{
						main.fadeTo(500, 1.0);
						return false;
					}

					var newImg = new Image();

					jQuery(newImg)
					.load(function(){
						var $this = jQuery(this);

						// have to insert image into dom to get height
						jQuery('body').append($this.hide());
						var newHeight = $this.height();
						$this.remove();

						if (newHeight != main_parent.height())
						{
							main_parent.animate
							(
								{height: newHeight}
							, 	400
							,	null
							,	function()
								{
									main.fadeTo(500, 1.0);
									main_parent.removeClass('loading');
								}
							);
						}
						else
						{
							main.fadeTo(500, 1.0);
							main_parent.removeClass('loading');
						}

					}).attr('src', $thumb.attr(attributes['src']));
				}
			);
				
			var image_src = $thumb.attr(attributes['src']).replace('&','%26');
			
			jQuery('#pinterest-button')
			.html('<a href="http://pinterest.com/pin/create/button/?url=' + window.location.href.split('#')[0] + 
				'&media=' + image_src + '" class="pin-it-button" count-layout="none">Pin It</a>');
			
			
			// Call function to load the Pinterest iframe again with new image to pin from gallery
			pinterest_load();

			options.callback($thumb);

			return false;
		});
}

/*
 * inspired by Alen Grakalic (http://cssglobe.com)
 */

jQuery.fn.imagePreview = function(o){

	var default_opt = {
		xOffset: 10
	,	yOffset: 30
	,	anchor: false
	,	disableLink: true
	,	attr: 'href'
	,	width: 250
	,	id: 'preview'
	,	alt_text: '&nbsp;'
	,	preload: false
	,	target: false
	,	position: 'bottom'
	}

	var options = jQuery.extend({}, default_opt, o);
	var $this = this;

	if (options.preload === true)
	{
		jQuery(function()
		{
			$this.each(function()
			{
				var anchor = jQuery(this);
				var src = jQuery(this).attr(options.attr);
				jQuery('<img />')
				.attr('src', src) // once we set the src, it will preload the image
				.load(function(){anchor.attr('img_loaded', '1');}); // flag the image as loaded so we don't try to use the load() event
			});
		});
	}

	// move all titles to a bogus param so it doesn't show fucky html in a popup on hover
	$this.each(
		function()
		{
			var anchor = jQuery(this);
			var title = anchor.attr('title') || '';

			anchor.attr('t', title);
			anchor.attr('title', ''); // unset this on hover so it doesn't look shitty
		}
	);

	// $("a.preview") // example target, a.preview with thumbnail inside
	$this
	.hoverIntent(
		function()
		{
			var anchor = jQuery(this);
			
			anchor.addClass('thumb_border_hover_outer');
			anchor.children('img').addClass('thumb_border_hover_inner');

			var title = anchor.attr('t') || '';

			// popup caption (can I make this take html?)
			var c = (title != "" && title != 'undefined') ? '<div class="caption">' + title + '</div>': "";
			var img = "<img src='"+ anchor.attr(options.attr) +"' alt='"+ options.alt_text +"' />";
			var preview = jQuery("<div id=" + options.id + ">" + img + c +"</div>");

			preview.add(jQuery('img', preview)).width(options.width);

			// wait until the image is loaded to show div so it's not empty
			// but once it's loaded once, we can't use the load event anymore, so just show it since the img is cached
			var img_loaded = anchor.attr('img_loaded') || false;

			if (parseInt(img_loaded) !== 1)
			{
				// wait until the image loads to display the popup
				jQuery('img', preview).load(
					function()
					{
						anchor.trigger('forceload');
					}
				);

				anchor.addClass('preview_waiting').bind
				(
					'forceload'
				,	function()
					{
						showImagePreview(anchor, preview, options);

						anchor.attr('img_loaded', '1').removeClass('preview_waiting');
					}
				);

				// mac chrome/safari don't like the image load event, so wait half a second and force the popup to display
				setTimeout("jQuery('a.preview_waiting').trigger('forceload');", 500);
			}
			else showImagePreview(anchor, preview, options);
	    },
		function()
		{
			var anchor = jQuery(this);

			anchor.removeClass('thumb_border_hover_outer');
			anchor.children('img').removeClass('thumb_border_hover_inner');
			
			var title = anchor.attr('t');
			anchor.attr('title', title);
			jQuery("#" + options.id).remove();
	    }
	);

    if (options.anchor === false)
    {
		$this.mousemove(
			function(e)
			{
				var preview = jQuery("#" + options.id);

				// TODO: add bounding so preview won't go offscreen
				var top = (e.pageY - parseInt(options.yOffset));
				var left = (e.pageX + parseInt(options.xOffset));

				preview.css({"top": top + "px", "left": left + "px"});
			});
	}

	// we don't want this link to open up image
	if (options.disableLink === true || options.attr == 'href')
	{
		$this.click(
			function(e)
			{
				e.preventDefault();
				e.stopPropagation();

				return false;
			});
	}
};

function showImagePreview(anchor, preview, options)
{
	// remove any lingering popups
	jQuery("#" + options.id).remove();

	jQuery("body").append(preview);

	var top = 0;
	var left = 0;

	if (options.target !== false)
	{
		var target = jQuery(options.target);
		var offset = target.offset();

		if (options.position == 'bottom')
		{
			top = offset.top + target.height();
			left = offset.left;
		}
	}
	else if (options.anchor === false)
	{
		top = (e.pageY - parseInt(options.yOffset));
		left = (e.pageX + parseInt(options.xOffset));
	}
	else
	{
		top = (parseInt(anchor.offset().top) - parseInt(options.yOffset));
		left = (parseInt(anchor.offset().left));

		var previewWidth = parseInt(jQuery('img', preview).css('width')) + 2;
		var previewHeight = parseInt(jQuery('img', preview).height()) + 2;

		var anchorWidth = anchor.width();

		var wnd = jQuery(window);
		var yPage = window.pageYOffset;

		// // popup rests on right side of thumb, unless it'd go offscreen to right
		if (left > jQuery('body').width()/2)
			left = left - previewWidth - parseInt(options.xOffset);
		else // then popup rests on right side of thumb
			left = left + anchorWidth + parseInt(options.xOffset);


		// if bottom of image below viewport, rest on bottom of viewport
		if ((top + previewHeight) > (wnd.height() +  yPage) && previewHeight < wnd.height())
			top = yPage + wnd.height() - previewHeight - 10;
		else if (previewHeight >wnd.height()) // then top of image at top of viewport
			top = yPage;
	}

	preview.css({"top": top + "px", "left": left + "px"}).fadeIn("normal");
}

jQuery.fn.hoverIntent = function(f,g,o) {
	// default configuration options
	var default_cfg = {
		sensitivity: 7,
		interval: 100,
		timeout: 0
	};

	o = o || {};
	var cfg = jQuery.extend(default_cfg, o);

	// override configuration options with user supplied object
	cfg = jQuery.extend(cfg, g ? {over: f, out: g} : f );

	// instantiate variables
	// cX, cY = current X and Y position of mouse, updated by mousemove event
	// pX, pY = previous X and Y position of mouse, set by mouseover and polling interval
	var cX, cY, pX, pY;

	// A private function for getting mouse position
	var track = function(ev) {
		cX = ev.pageX;
		cY = ev.pageY;
	};

	// A private function for comparing current and previous mouse position
	var compare = function(ev,ob) {
		ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
		// compare mouse positions to see if they've crossed the threshold
		if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) {
			jQuery(ob).unbind("mousemove",track);
			// set hoverIntent state to true (so mouseOut can be called)
			ob.hoverIntent_s = 1;
			return cfg.over.apply(ob,[ev]);
		} else {
			// set previous coordinates for next time
			pX = cX;pY = cY;
			// use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
			ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval );
		}
	};

	// A private function for delaying the mouseOut function
	var delay = function(ev,ob) {
		ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
		ob.hoverIntent_s = 0;
		return cfg.out.apply(ob,[ev]);
	};

	// A private function for handling mouse 'hovering'
	var handleHover = function(e) {
		// next three lines copied from jQuery.hover, ignore children onMouseOver/onMouseOut
		var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
		while ( p && p != this ) {try {p = p.parentNode;} catch(e) {p = this;}}
		if ( p == this ) {return false;}

		// copy objects to be passed into t (required for event object to be passed in IE)
		var ev = jQuery.extend({},e);
		var ob = this;

		// cancel hoverIntent timer if it exists
		if (ob.hoverIntent_t) {ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);}

		// else e.type == "onmouseover"
		if (e.type == "mouseover") {
			// set "previous" X and Y position based on initial entry point
			pX = ev.pageX;pY = ev.pageY;
			// update "current" X and Y position based on mousemove
			jQuery(ob).bind("mousemove",track);
			// start polling interval (self-calling timeout) to compare mouse coordinates over time
			if (ob.hoverIntent_s != 1) {ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}

		// else e.type == "onmouseout"
		} else {
			// unbind expensive mousemove event
			jQuery(ob).unbind("mousemove",track);
			// if hoverIntent state is true, then call the mouseOut function after the specified delay
			if (ob.hoverIntent_s == 1) {ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
		}
	};

	// bind the function to the two event listeners
	return this.mouseover(handleHover).mouseout(handleHover);
};

/**
 * Capture click event, only execute if 'OK' is clicked in the confirm box
 * 
 * msg - Confirm message string
 * o - passes options to jQuery.prompt (jquery.impromptu.js)
 */
jQuery.fn.confirm = function(msg, o)
{
	if ( ! msg || msg == '')
		msg = 'Are you sure?';

	o = o || {};

	var def_options =
		{
			buttons:
			{
				Cancel: false
			,	Ok: true
			}
		, 	overlayspeed:'fast'
		,	submit: finishClick
		, 	promptspeed:'fast'
		, 	show:'fadeIn'
		};

	var options = jQuery.extend({}, def_options, o);

	var buttons = jQuery(this);
	buttons.mousedown(
		function(e)
		{
			e.preventDefault();
			e.stopPropagation();

			var $_this = jQuery(this); // only the button that was clicked, not the group of buttons passed to .confirm();

			// add selected flag so callback can trigger it
			$_this.addClass('click_flag');

			jQuery.prompt(msg, options);

			return false;
		}
	).click(function(e){e.preventDefault();return false;});
}

function finishClick(v,m)
{
	var clicked = jQuery('.click_flag');

	if (v == 'true')
	{
		clicked.unbind('click');

		clicked.closest('form').unbind();

		if (clicked.is('a'))
			document.location = clicked.attr('href');
		else clicked.click(); // if submit button or something
	}
	else
	{
		if (clicked.hasClass('delete_item'))
			jQuery('#delete_item_ie').val('');
		
		clicked.removeClass('click_flag');
	}

	return true;
}

jQuery.fn.clickClass = function(class_name)
{
	var $this = this;

	// if use mouses off before finishing click, but then goes back, keep class, use this as a placeholder
	var mouseoff_class = class_name + '_mouseoff';

	$this.mousedown // add when clicked
	(
		function()
		{
			jQuery(this).addClass(class_name);
			jQuery('.' + class_name).removeClass(class_name);
		}
	).mouseup // remove when click is done
	(
		function()
		{
			jQuery('.' + class_name + ', .' + mouseoff_class).removeClass(class_name).removeClass(mouseoff_class);
		}
	).mouseout // or remove if use mouses off before finishing click
	(
		function()
		{
			var _$this = jQuery(this);

			if(_$this.hasClass(class_name))
			{
				_$this.removeClass(class_name).addClass(mouseoff_class);
				setTimeout("jQuery('." + mouseoff_class + "').removeClass('" + mouseoff_class + "');", 5000);
			}
		}
	).mouseout // and add it back if use mouses back on
	(
		function()
		{
			var _$this = jQuery(this);
			if (_$this.hasClass(mouseoff_class))
				_$this.addClass(class_name).removeClass(mouseoff_class);
		}
	);
}

// validate email input, pass jquery object of input, and id of url for ajax validation
function validate_email($this, o)
{
	var o = o || {};

	var def_options = {
		'email_url_id': 'validate_email_url'
	,	'pass': function(data)
		{
			$this.attr('valid', '1').attr('last_email', $this.val());
			$this.siblings('.error_message, .notice_message').hide();
			jQuery('#existing_email_error').hide();
			jQuery('#current_email_error').hide();
		}
	,	'fail': function(data)
		{
			$this.attr('valid', '0');
			$this.siblings('.error_message').show();
			jQuery('#existing_email_error').hide();
			jQuery('#current_email_error').hide();
		}
	,	'exists': function(data)
		{
			$this.attr('valid', '0');
			$this.siblings('.error_message').hide();

			var error_message = jQuery('#existing_email_error');

			error_message.show();
		}
	,	'params': {}
	};

	var options = jQuery.extend({}, def_options, o);

	var last_email = $this.attr('last_email') || '';
	var email = $this.val();
	var already_valid = $this.attr('valid') == 1 || $this.attr('valid') == '1';

	//console.log('last: "' + last_email + '" - current: "' + email + '" - already_valid: "' + (already_valid ? 'true' : 'false') + '"');

	if (last_email.length > 0 && last_email == email && already_valid)
	{
		options.pass();
		return;
	}

	var emailRegex = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
	if (email.match(emailRegex))
	{
		var validate_email_link = jQuery('#' + options.email_url_id)

		// if for some reason we can't get url for ajax validation, let it pass, action will return dupe error
		if (validate_email_link.length > 0)
			var validate_email_url = validate_email_link.attr('href');
		else
		{
			options.pass();
			return;
		}

		var params = jQuery.extend({}, {"email": email}, options.params);

		jQuery.post
		(
			validate_email_url
		,	params
		,	function(data)
			{
				var fail = (data == 0 || data == '0') || data.length == 0;

				if (fail)
				{
					options.fail(data);
				}
				else if (data == 'exists')
				{
					options.exists(data);
				}
				else
				{
					options.pass(data);
				}
			}
		);
	}
	else
	{
		options.fail();
		return;
	}
}

/*
 * pass jquery object for zip input, can pass zip_url link id, return format (html|json|etc), pass/fail callbacks, and extra params
 */
function validate_zip($this, opt)
{
	// post to validate_zip_url
	// get return, if false, display error message
	// if string, set valid_zip_location.html(ret)
	var valid_zip_location = $this.siblings('.valid_zip_location');

	var zipready = jQuery('#guest_zipbuttonSave_zip')

	var def_options = {
		'zip_url_id': 'validate_zip_url'
	,	'ajax_format': 'html'
	,	'pass': function(data)
		{
			$this.attr('last_zip', current_zip).attr('valid', '1').siblings('.error_message').hide();
			jQuery("div[id$='zip_error']").hide();
			valid_zip_location.html(data).css('opacity', '1');
			if (zipready.hasClass('awaiting_zip'))
				zipready.click();
			return;
		}
	,	'fail': function(data)
		{
			$this.attr('valid', '0').siblings('.error_message').show();
			jQuery("div[id$='zip_error']").show();
			valid_zip_location.css('opacity', '.01'); // in case they change it to an invalid zip

			return;
		}
	,	'params': {}
	};

	// in case nothing extra is passed
	var o = opt || {};

	var options = jQuery.extend({}, def_options, o);

	var validate_zip_url = jQuery('#' + options.zip_url_id).attr('href');

	var last_zip = $this.attr('last_zip') || '';
	var current_zip = $this.val();

	var already_valid = $this.attr('valid') == 1 || $this.attr('valid') == '1';

	// in case they edited and put it back the same, don't validate again
	if (last_zip.length > 0 && last_zip == current_zip && already_valid)
		return;

	var zip_pass = false;

	// check US zip
	if (current_zip.match(/^[0-9]{5}$/))
		zip_pass = true;

	// check CAN postal codes (without space)
	if ( ! zip_pass)
	{
		var zip_check = current_zip.toUpperCase();
		if (zip_check.match(/^[A-Za-z][0-9][A-Za-z][0-9][A-Za-z][0-9]$/))
			zip_pass = true;
		// and with a space/hyphen/etc
		if (zip_check.match(/^[A-Za-z][0-9][A-Za-z].[0-9][A-Za-z][0-9]$/))
			zip_pass = true;
	}

	if ( ! zip_pass)
	{
		$this.attr('valid', false).next('.error_message').show();
		valid_zip_location.css('opacity', '.01');

		return;
	}

	// allow params to be passed
	var params = jQuery.extend({}, {"zip": current_zip}, options.params);

	jQuery.post
	(
		validate_zip_url
	, 	params
	, 	function(data, textStatus)
		{
//			var fail = options.ajax_format == 'html'
//				? (data == 0 || data == '0') || data.length == 0
//				: data.length == 0 || data == 0

			var fail = (data == 0 || data == '0') || data.length == 0;

			if (fail)
			{
				options.fail(data);
			}
			else
			{
				options.pass(data);
			}
		}
	,	options.ajax_format
	);
}

jQuery.fn.externalLink = function(favicon)
{
	var use_favicon = favicon || false;

	this
	.click(
		function(event)
		{
			event.preventDefault();
			jQuery(this).blur();
			window.open(jQuery(this).attr('href'));
			return false;
		}
	);

	// don't put an external icon next to an image
	var links = this.filter(function(){return jQuery(this).children('img').length < 1;});

	if (use_favicon)
	{
		links.faviconLink();
	}
	else links.addClass("external");
}

// use an external website's favicon as the external icon
jQuery.fn.faviconLink = function()
{
	var links = this;
	jQuery.each(links, function()
	{
		var link = this;

		var hoststring = /^http:/;
		var hrefvalue = link.getAttribute("href",2);

		if (hrefvalue.search(hoststring) != -1)
		{
			var domain = hrefvalue.match(/(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)/);
			var domain = RegExp.$2;

			var cue = document.createElement("img");
			cue.className = "faviconimg";
			//var cuesrc = "http://" + domain + "/favicon.ico";
			var cuesrc = "http://ww.google.com/s2/favicons?domain=" + domain
			cue.setAttribute("src", cuesrc);

			// this should default to external gif if no favicon is found
			cue.onerror = function () {
				this.src = "/images/fileicons/external.gif";
			}

			// now append the favicon image
			link.appendChild(cue);
		}
	});
}

jQuery.fn.offHoverDelay = function(opts)
{
	var default_options = {
		duration: 1000
	,	callback: function(){}
	};

	var options = jQuery.extend({}, default_options, opts);

	var $this = this;

	var rename_me = function()
		{
			options.callback();
			$this.css('opacity', 1).show();
		};

	$this.fadeOut(options.duration, rename_me)
		.hoverIntent(function(){$this.stop().animate({opacity: 1}, 200);}, function(){});
};

/**
 * handle toggling of menus, use ajax to save state to session if 'update_session' passed as true
 */
jQuery.fn.handleToggle = function(update_session)
{
	update_session = update_session || true;

	var $this = this;
	var target = $this.next();
	var target_id = target.attr('id');
	var open_text = jQuery('span.open_text', $this);

	// toggle next element and change class of trigger accordingly
	$this.toggleClass('open').toggleClass('closed');

	// after we toggle the classes, determine what state it's in
	var ele_state = $this.hasClass('open') ? 'open' : 'closed';

	// if we close a menu and it has a pch-style popout, close it as well
	if (ele_state == 'closed')
		jQuery('.hover_highlight', target).qtip('hide');

	target.slideToggle(200, function(){if (open_text.length > 0) open_text.toggle();});

	// post id of element to navMemory action
	// going to use a bitmask, will make this a lot simpler, where should i store the bitmask info? yaml?
	if ( target_id.length > 0 && update_session === true )
		jQuery.post('/nav/navMemorySet', {ele: target_id, state: ele_state});
}

jQuery.html_decode = function(encoded)
{
	var decoded = jQuery('<textarea />').html(encoded).val();
	return decoded;
}

jQuery.strcasecmp = function (f_string1, f_string2){
    // Binary safe case-insensitive string comparison
    //
    // version: 1004.1212
    // discuss at: http://phpjs.org/functions/strcasecmp
    // +     original by: Martijn Wieringa
    // +     bugfixed by: Onno Marsman
    // *         example 1: strcasecmp('Hello', 'hello');
    // *         returns 1: 0
    var string1 = (f_string1+'').toLowerCase();
    var string2 = (f_string2+'').toLowerCase();

    if (string1 > string2) {
      return 1;
    }
    else if (string1 == string2) {
      return 0;
    }

    return -1;
}

jQuery.fn.flash = function(flash_background)
{
	var is_background = flash_background || false;

	var attr = is_background ? 'backgroundColor' : 'color';

	var $this = jQuery(this);

	var origColor = $this.css(attr) || 'unset';
	var unset = origColor == 'unset' || origColor == 'rgba(0, 0, 0, 0)';

	// set background back to transparent if it's not specifically set
	if (unset && is_background)
		origColor = 'transparent';

	// don't set text color to transparent though, use black I guess...
	if ( ! is_background && unset)
		origColor = '#000000';

	// need to build these options arrays ahead of time since the key can change and js sucks with arrays
	var start_options = {};
	start_options[attr] = '#ffffbb';
	
	var end_options = {};
	end_options[attr] = origColor;

	$this.animate(start_options, 450, 'linear',function(){$this.animate(end_options, 450 );});
}

/**
 * create a text input with target element's text as default value
 *
 * cancel button destroys input and leaves original element
 *
 * @param regex string (used for validation input value, prompts user to fix before allowing to save)
 */
jQuery.fn.inlineEdit = function(regex)
{
	// creates a text input with this elements text as default value
	// input name can use element's title attr

	var $this = this;
	//regex = new RegExp(regex, 'gi');

	$this.addClass('mouseover');

	$this.click(function()
	{
		var ele = jQuery(this);
		init_inline_edit(ele, regex);
	});
}

function init_inline_edit(ele, regex)
{
	var title = ele.attr('title');
	var index = jQuery('body').index(ele);
	var input = jQuery('<input>');

	input.attr('name', title);
	input.attr('id', title + '_' + index); // give this a unique id
	input.attr('maxlength', 100);
	input.addClass('inlineEdit');

	// remove any tabs from html formatting
	// check for (empty) and (null) flags, if exist, set to empty string
	var default_value = ele.text().replace(/\t/gi, '');
	if (default_value.indexOf('(null)') != -1 || default_value.indexOf('(empty)') != -1)
		default_value = '';

	input.val(default_value);

	var cancelLink = jQuery('<span>Cancel</span>');
	cancelLink.addClass('cancelEdit').addClass('mouseover');
	cancelLink.click(function()
	{
		var _$this = jQuery(this);

		ele.show();
		_$this.add(_$this.siblings('input.inlineEdit')).remove();
	});

	// to validate
	input.blur(function()
	{
		var string = input.val();
		var matches = string.match(regex);
		//console.log(matches);

		// don't let them do shit
		if (matches && matches.length > 0)
		{
			jQuery.prompt
			(
				'There are invalid characters in the description. Please remove them.'
			,	{
					callback: function()
					{
						input.focus();
					}
				,	buttons:
					{
						'OK': true
					,	'Undo Changes': false
					}
				,	submit: function(v, m)
					{
						if (v === false)
							cancelLink.click();

						return true
					}
				}
			);
		}
	});

	var inline = input.add(cancelLink);

	//console.log(inline);

	ele.after(inline);
	ele.hide();

	input.focus();
}


/**
 * determine if an element (usually a popup) is part/all outside of viewport
 * if it is, scroll the window to fully display the element
 */
function scroll_to_element(element)
{
	var wnd = jQuery(window);
	var offset = element.offset();
	var yPage = window.pageYOffset;
	var xPage = window.pageXOffset;
	var y = yPage;
	var x = xPage;

	/*
	alert("offTop: " + offset.top +
		  "\noffLeft: " + offset.left +
		  "\nyPage: " + yPage +
		  "\nxPage: " + xPage +
		  "\nwndHt: " + wnd.height() +
		  "\nwndWdth: " + wnd.width());
		  */

	//if popup bottom is below or popup top is above
	if ((offset.top + element.height()) > (wnd.height() +  yPage) || offset.top < yPage)
		y = offset.top - wnd.height()/2;
	//if popup right is to right of popup left is to left
	if ((offset.left + element.width()) > (wnd.width() + xPage) || offset.left < xPage)
		x = offset.left;

	if (y != yPage || x != xPage)
		window.scrollTo(x, y);
}

jQuery.formatDollar = function formatDollar(n)
{
	x = parseFloat(n)*100;
	// for some reason 3x.7 is evaling to 2.09999999999, round this bitch!
	x = Math.round(x);
	dollars = parseInt(n);
	cents = (x - dollars*100) + '' //turns var into string
	if (cents == '0' || cents == '') cents = '00';

	return dollars + '.' + cents;
}

jQuery.ucwords = function(str, force_format) {
	// Uppercase the first character of every word in a string
	//
	// version: 1001.2911
	// discuss at: http://phpjs.org/functions/ucwords

	// +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
	// +   improved by: Waldo Malqui Silva
	// +   bugfixed by: Onno Marsman
	// +   improved by: Robin
	// *     example 1: ucwords(‘kevin van zonneveld’);    // *     returns 1: ‘Kevin Van Zonneveld’
	// *     example 2: ucwords(‘HELLO WORLD’);
	// *     returns 2: ‘HELLO WORLD’

	force_format = force_format || false;
	if (force_format == true)
		str = str.toLowerCase();

	return (str + '').replace(/^(.)|\s(.)/g, function ($1) {return $1.toUpperCase();});
}

// use pch message div to display ajax return message
// timeout in seconds, defaults to 60
jQuery.pchMessage = function(message, status, timeout)
{
	timeout = (timeout || 60)*1000; // convert to ms

	if (message.length == 0)
		return;

	message = jQuery('<div>' + message + '</div>');
	message.addClass(status);

	//console.log(message);

	jQuery('#message').html(message).show();

	setTimeout("jQuery('#message').html('').hide()", timeout);
}

// returns array of arbitrary form inputs that aren't necessarily inside a form
jQuery.fn.serializeMe = function()
{
	var inputs = this;
	var data = {};

	inputs.each(function(){
		var $this = jQuery(this);
		var name = $this.attr('name');
		var val = $this.val();

		var include = true;

		var type = $this.attr('type');
		if(type == 'checkbox' && ! $this.is(':checked'))
			include = false;

		if(include)
			data[name] = val;
	});

	return data;
}

/**
 * allow a user to toggle between hidden and plaintext password inputs for easy verification
 * create a plain input if one isn't set up already
 * create a toggle link if it doesn't exist
 */
jQuery.fn.showpass = function()
{
	var input = this;

	if ( ! input.hasClass('hidden_pass'))
		input.addClass('hidden_pass');

	// we'll need to create the input to hold the plaintext version of the password (unless it's already generated in html
	var plain_input = input.siblings('input.plain_pass');
	if (plain_input.length == 0)
	{
		// take all the classes and such from the hidden pass input and convert them to the plain versions
		var plain_input_class = input.attr('class').replace('pass_', 'pass_plain_').replace('required', '').replace('hidden_pass', 'plain_pass');
		var plain_input_attrs = {
			'id': 'plain_' + input.attr('id')
		,	'name': 'plain_' + input.attr('name')
		,	'class': plain_input_class
		,	'style': 'display:none;'
		};

		plain_input = jQuery('<input>').attr(plain_input_attrs).insertAfter(input);
	}

	// look for an existing show/hide link, so we only create it once
	var toggle_input = input.siblings('a.show-hidden, a.show-plain');

	if (toggle_input.length == 0)
	{
		var container = input.parent();

		jQuery("<a>")
		.text("Show Password")
		.addClass("show-plain")
		.attr
		(
			{
				title: "Show the password in plain text"
			,	href: "#"
			}
		)
		.prependTo(container);
	}


	jQuery("a.show-plain")
	.live
	(
		"click"
	,	function()
		{
			var link = jQuery(this);
			var input = link.siblings('input.hidden_pass');
			var plain_input = link.siblings('input.plain_pass');

			// automatically switch back to hidden input when plain input loses focus
			// also submit form when hitting enter on plain input
			plain_input
			.bind
			(
				"blur"
			,	function(e)
				{
					plain_input.siblings('a.show-hidden').click();
				}
			)
			// whenever any key is pressed when this input has focus, update the hidden one
			.keyup
			(
				function(e)
				{
					input.val(plain_input.val())
					if (e.keyCode == 13)
					{
						jQuery(this).closest('form').find('input.submit').click();
						return;
					}
				}
			)
			.val(input.val())
			.show();

			input.hide();

			link
			.html("Hide Password")
			.removeClass("show-plain")
			.addClass("show-hidden")
			.attr
			(
				{
					title: "Mask password for security"
				}
			);

			return false;
		}
	);

	jQuery("a.show-hidden")
	.live
	(
		"click"
	,	function()
		{
			var link = jQuery(this);
			var input = link.siblings('input.hidden_pass');
			var plain_input = link.siblings('input.plain_pass');

			// whenever any key is pressed when this input has focus, update the plain one
			input
			.keyup
			(
				function(e)
				{
					plain_input.val(input.val())
					if (e.keyCode == 13)
					{
						jQuery(this).closest('form').find('input.submit').click();
						return;
					}
				}
			)
			.val(plain_input.val())
			.show();


			plain_input.hide();

			link
			.text("Show Password")
			.removeClass("show-hidden")
			.addClass("show-plain")
			.attr
			(
				{
					title: "Show the password in plain text"
				}
			)

			return false;
		}
	);
}

// refresh list of shipping methods when zip is changed
function refresh_shipping_method($this)
{
	var zip = $this.val();
	var shipping_method_update_url = jQuery('#shipping_method_update_url').val();
	var is_shipping_page = jQuery('#is_shipping_page').val();

	var current_method_select = jQuery('#shipping_method_select');

	// hide method select
	current_method_select.children().fadeTo('fast', '.01').end().addClass('loading');

	jQuery.post
	(
		shipping_method_update_url
	, 	{"zip": zip, 'is_shipping_page': is_shipping_page}
	, 	function(data, textStatus)
		{
			if ( ! data.length > 0)
			{
				// just don't refresh it? put up error message or something
				//alert('failed');

				jQuery('#shipping_method_select').removeClass('loading');

				return false;
			}
			else
			{
				// replace current shipmethod html with html from post
				var new_method_select = jQuery(data).html();

				current_method_select.html(new_method_select);

				// replace all event handlers for method select
				init_shipping_method_select();

				jQuery('#shipping_method_select').removeClass('loading');

				return true;
			}
		}
	);

	setTimeout("jQuery('#shipping_method_select').removeClass('loading');", '8000');
}

function init_shipping_method_select()
{
	// edit button for method select toggle
	// is that it?
	jQuery('#shipping_method_select_toggle')
	.click
	(
		function(e)
		{
			e.preventDefault();
			e.stopPropagation();

			jQuery('table.ship_methods').slideDown();
			jQuery('table.selected_method').add(jQuery(this)).hide();

			return false;
		}
	);

	var triggerme = jQuery('#shipping_rate_calc_notice');

	triggerme.mousedown(function(e){
		e.preventDefault();
		e.stopPropagation();
	});

	var shipping_grid = jQuery('#pricing_grid');
	var shipping_grid_options =
		{
				modal : true
			,	dialogClass : 'downinc_pop'
			,	minHeight : 310
			,	width :	420
			,	resizable : false
			,	show_title : true
			,	title : 'Shipping Rates'
		}
	shipping_grid.pchDialog( triggerme ,shipping_grid_options);

	// if we're on the cart page, we want this ship method to persist to billing/shipping but createRetailAccount is never getting this
	// update the guest checkout form with this value so it can pass it down the line
//	var guest_ship_method = jQuery('#guest_ship_method');
//	if (guest_ship_method.length > 0)
//	{
//		jQuery('table.ship_methods input.radiobutton').mouseup(
		jQuery('select#ship_method').change(
			function()
			{
				var $this = jQuery(this);
				var shipping_method_select = jQuery('#shipping_method_select');
				
				shipping_method_select.addClass('loading');

				jQuery.post
				(
					'/update-shipmethod'
				, 	{ship_method: $this.val()}
				, 	function(data, textStatus)
					{
						var fail = (data == 0 || data == '0') || data.length == 0;

						if (fail)
						{
							shipping_method_select.removeClass('loading');
						}
						else
						{
							if (jQuery('tr.shipping_total td.price_subtotal').size() > 0)
							{
								var cart = jQuery('#cartitems');
								
								// get old total, store element, and grab amount.
								var total_ele = cart.find('tr.shipping_total td.price_subtotal');
								total_ele.text(jQuery.trim(total_ele.text()));
								var old_total = parseFloat(total_ele.text().substring(1));

								// grab the old shipping, then new - we'll add the difference to old_total
								var ship_ele = cart.find('tr.shipping td.price_subtotal');
								ship_ele.text(jQuery.trim(ship_ele.text()));
								var old_ship_price = ship_ele.text().substring(1);
								var old_ship_float = parseFloat(old_ship_price);


								var new_ship_price = $this.children('option').filter(':selected').text()
									.match(/\$[0-9]+.[0-9]+/).toString().substring(1);
								var new_ship_float = parseFloat(new_ship_price);

								var new_cart_float = old_total - old_ship_float + new_ship_float;

								if (isNaN(new_cart_float)) 
									new_cart_float = '0.00';

								// now update our 2 elements...
								ship_ele.html('$' + formatDollar(new_ship_float));
								total_ele.html('$' + formatDollar(new_cart_float));
							}
							shipping_method_select.removeClass('loading');
						}
					}
				,	'html'
				);
			}
		)
//	}

	// if user submits zip update, do it ajax since it's already set up for this behavior on billing/shipping
	var zip_input = jQuery('#update_guest_zip');
	if (zip_input.length > 0)
	{
		zip_input.keypress
		(
			function(e)
			{
				if (e.which == 13)
				{
					e.preventDefault();

					refresh_shipping_method(zip_input);

					return false;
				}
			}
		);

		var zip_submit = jQuery('#update_zip_button');
		zip_submit.click
		(
			function(e)
			{
				e.preventDefault();

				refresh_shipping_method(zip_input);

				return false;
			}
		);
	}

}

// select text in an input/textarea
// we can set cursor position by omitting end
jQuery.fn.selectRange = function(start, end) {
	if (typeof(end) == 'undefined')
		end = start;

    return this.each(function() {
        if (this.setSelectionRange) {
            this.focus();
            this.setSelectionRange(start, end);
        } else if (this.createTextRange) {
            var range = this.createTextRange();
            range.collapse(true);
            range.moveEnd('character', end);
            range.moveStart('character', start);
            range.select();
        }
    });
};

function init_form_validation()
{
	jQuery('.required, .require_me').not('.validationengine, .noengine').each
	(
		function()
		{
			var $this = jQuery(this);
			var class_attr = $this.attr('class');
			var container = $this.closest('div.ctrlHolder');
			var is_grouped = container.hasClass('group_required');

			var req_class = 'validate[';
			if (is_grouped)
			{

				if (container.hasClass('phone'))
					container.find('.required').blur(function(){phone_required(jQuery(this));});
				else container.find('.required').blur(function(){group_required(jQuery(this));});

				return;
			}

			if ( ! is_grouped)
			{
				req_class += 'required';
			}


			req_class += ']';

			// require_me is for inputs that are conditionally required. if require_me and ! require, remove
			if ( ! $this.hasClass('required') && $this.hasClass(req_class))
				$this.removeClass(req_class).removeClass('validationengine');
			else if ($this.hasClass('required'))
			{
				$this.attr
				(
					'class'
				,	req_class + ' ' + class_attr
				)
				.addClass('validationengine');
			}
			else return;

			// force the form to be re-evaluated
			$this.closest('form').removeClass('validationengineform').validationEngine('hide').validationEngine('detach');
		}
	);

	jQuery('form').not('.validationengine').each
	(
		function()
		{
			var $this = jQuery(this);
			var form_id = $this.attr('id') || false;

			if ($this.find('.validationengine') && form_id !== false)
				$this.validationEngine('attach', {promptPosition : "centerRight", scroll: false}).addClass('validationengineform');
		}
	)
}

jQuery.fn.vmessage = function(msg, o)
{
	var def_options = {
		'type': 'error' // either 'pass', 'load', or {anything} for error
	,	'position': 'centerRight'
	,	'showTip': true
	};

	var options = jQuery.extend({}, def_options, o);

	this.validationEngine('showPrompt', msg, options.type, options.position, options.showTip)

	return this;
}

function hide_ve_messages()
{
	jQuery('form').each(function(){jQuery(this).validationEngine('hide')});
}

function group_required($this)
{
	var container = $this.closest('.group_required');
	var required_fields = container.find('.required');

	var error_message = container.find('div.error_message');
	var message = error_message.length > 0
		? error_message.html()
		: 'Please fill in all of the following fields';

	var empty_fields = required_fields.filter(function(){return jQuery(this).val().length == 0});

	if (empty_fields.length > 0)
		container.find('.required:last').vmessage(message);
	else container.find('.required:last').validationEngine('hide');
}

function phone_required($this)
{
	var container = $this.closest('.group_required');
	var required_fields = container.find('.required');

	var error_message = container.find('div.error_message');
	var message = error_message.length > 0
		? error_message.html()
		: 'Enter a valid phone number';

	var phonenum = '';
	required_fields.each(function(){phonenum += jQuery(this).val();});

	if (phonenum.length != 10)
	{
		container.find('.required:last')
		.data('promptPosition', 'centerRight:+80') // bump this out past ext: field
		.vmessage(message, {  });
	}
	else container.find('.required:last').validationEngine('hide');
}

function pinterest_load()
{
	var s = document.createElement("script");
	s.type = "text/javascript";
	s.async = true;
	s.src = "http://assets.pinterest.com/js/pinit.js";
	var x = document.getElementsByTagName("script")[0];
	x.parentNode.insertBefore(s, x);
}
