
/* ==================================================
=====================================================
                Global Variables
=====================================================
================================================== */

// modal state
var modalState = 0; // disabled
// counter for loading image
var loadingID = 0;
// preload the loading image
var preloadImage = $('<img />').attr({'src':'static/images/ajax-loader.gif'});

/* ==================================================
=====================================================
                Extend jQuery Functions
=====================================================
================================================== */

// validation
jQuery.extend({
	isEmail: function(text) {
		var emailReg = /^[a-zA-Z_0-9\.\-]+\@([a-zA-Z_0-9\-]+\.)+[a-zA-Z_0-9\-]+$/;
		return emailReg.test($.trim(text));
	},

	isVBPassword: function(text) {
		var regex = /^[a-zA-Z0-9]{5,10}$/;
		return regex.test($.trim(text));
	}
});

/* ==================================================
            N E W  -  S T I C K Y   C A R T
================================================== */

(function( $ ){

	$.fn.containedStickyScroll = function( options ) {

		var defaults = {
			oSelector : this.selector,
			unstick : true,
			easing: 'linear',
			duration: 500,
			queue: false,
			closeChar: '^',
			closeTop: 0,
			closeRight: 0	
		}	

		var options = $.extend(defaults, options);

		if(options.unstick == true){	
			this.css('position','relative');
			this.append('<a class="scrollFixIt">' + options.closeChar + '</a>');
			jQuery(options.oSelector + ' .scrollFixIt').css('position','absolute');
			jQuery(options.oSelector + ' .scrollFixIt').css('top',options.closeTop + 'px');
			jQuery(options.oSelector + ' .scrollFixIt').css('right',options.closeTop + 'px');
			jQuery(options.oSelector + ' .scrollFixIt').css('cursor','pointer');
			jQuery(options.oSelector + ' .scrollFixIt').click(function() {
				getObject = options.oSelector;
				jQuery(getObject).animate({ top: "0px" },
					{ queue: options.queue, easing: options.easing, duration: options.duration });
				jQuery(window).unbind();
				jQuery('.scrollFixIt').remove();
			});
		} 
		jQuery(window).scroll(function() {
			getObject = options.oSelector;
			if(jQuery(window).scrollTop() > (jQuery(getObject).parent().offset().top) &&
				 (jQuery(getObject).parent().height() + jQuery(getObject).parent().position().top - 30) > (jQuery(window).scrollTop() + jQuery(getObject).height())){
				jQuery(getObject).animate({ top: (jQuery(window).scrollTop() - jQuery(getObject).parent().offset().top) + "px" }, 
					{ queue: options.queue, easing: options.easing, duration: options.duration });
			}
			else if(jQuery(window).scrollTop() < (jQuery(getObject).parent().offset().top)){
				jQuery(getObject).animate({ top: "0px" },
					{ queue: options.queue, easing: options.easing, duration: options.duration });
			}
		});

	};
})( jQuery );

/* ==================================================
=====================================================
                Prototypes
=====================================================
================================================== */

/**
 * Helper to markup price displays
 */
Number.prototype.ppPriceMarkup = function(commas) {
	var nStr = this.toFixed(2) + '';
	var x = nStr.split('.');
	var x1 = x[0];
	var x2 = (x.length > 1) ? x[1] : '00';

	if (commas == true) {
		var rgx = /(\d+)(\d{3})/;
		while (rgx.test(x1)) {
			x1 = x1.replace(rgx, '$1' + ',' + '$2');
		}
	}

	var r = '';
	if (x1.substr(0, 1) == '-') {
		r += '<span class="operation">-</span>';
		x1 = x1.substr(1);
	}

	r += '<span class="currency">$</span>';
	r += '<span class="dollars">' + x1 + '</span>';
	r += '<span class="cents">.' + x2 + '</span>';

	return r;
};

/**
function EDITER_DIVIDER(){} ========================================================================
 */

/**
 * Log to Firebug console
 */
function log(message) {
	if (window.console && typeof window.console.log == "function") {
		console.log(message);
	}
}

/**
 * Show a loading image anytime an ajax call is fired
 */
function ajaxShowLoading(thisEl, thisID){
	var offset = thisEl.offset();
	var x = (offset.left + (thisEl.outerWidth()/2) - 15) + 'px';
	var y = (offset.top + (thisEl.outerHeight()/2) - 15) + 'px';
	preloadImage.clone().attr({'id':'ajaxLoading'+thisID}).css({'display':'none', 'position':'absolute', 'left':x, 'top':y, 'z-index':'9999'}).appendTo($('body'));
	$('#ajaxLoading'+thisID).fadeIn();
}

/**
 * Hide the loading image when the ajax call returns
 */
function ajaxHideLoading(thisID){
	$('#ajaxLoading'+thisID).fadeOut(400, function() {
		$(this).remove();
	});
}

/**
 * Common information to display when we have a failed response
 */
function commonErrorDisplay(XMLHttpRequest, textStatus, errorThrown) {
	if (textStatus == 'timeout')
		alert('Timeout');
	else if (textStatus == 'error') {
		if (XMLHttpRequest.status == 400)
			alert('400 Bad Request');
		else if (XMLHttpRequest.status == 404)
			alert('404 Not Found');
		else if (XMLHttpRequest.status == 500)
			alert('500 Internal Server Error');
	}
}

/**
 * Common information to display when response has valid xml
 */
function commonSuccessDisplay(el, data) {

	var noticeMsg = '';
	var errorMsg = '';
	var warningMsg = '';

	$(data).find('NOTICE').find('NOTICE_ROW').each(function() {
		noticeMsg += " - " + $(this).text() + "\n";
	});

	$(data).find('ERROR').find('ERROR_ROW').each(function() {
		errorMsg += " - " + $(this).text() + "\n";
	});

	$(data).find('WARNING').find('WARNING_ROW').each(function() {
		warningMsg += " - " + $(this).text() + "\n";
	});

	if (noticeMsg != '' || errorMsg != '' || warningMsg != '') {
		var msg = '';
		if (noticeMsg != '')
			msg += "Notices:\n" + noticeMsg + "\n";
		if (errorMsg != '')
			msg += "Errors:\n" + errorMsg + "\n";
		if (warningMsg != '')
			msg += "Warnings:\n" + warningMsg + "\n";
		alert(msg);
	}

	// TODO: hide this debugging output
//	showDebugOutput(el, $(data).find('SQLOUTPUT').text());
//	showDebugOutput(el, $(data).find('UNKNOWNOUTPUT').text());
}

/**
 * Helper to show debugging output
 */
function showDebugOutput(el, text) {
	if (text != '') {
		text = '<a href="#" onClick="this.parentNode.style.display=\'none\'; return false;">(Hide AJAX Results)</a>' + text;

		var offset = el.offset();
		var markup = {'border':'2px solid #000000', 'background-color':'#f8f8f8', 'padding':'10px',
			'position':'absolute', 'top':offset.top, 'left':offset.left, 'z-index':'9999'};
		$('<div></div>').html(text).css(markup).appendTo($('body'));
	}
}

/**
 * Helper to get a url's vars
 */
function getUrlVars(url){
	// drop everything after and including #
	url = url.split('#')[0];

	// drop everything before and including ?
	if (url.indexOf('?') != -1)
		url = url.split('?')[1];

	// save all key/value pairs into an object
	var vars = {};
	url.replace(/([^=&]+)=([^&]*)/gi, function(m, key, value) {
		vars[key] = value;
	});

	return vars;
}

/**
function EDITER_DIVIDER(){} ========================================================================
 */

/**
 * Get value of a given url var
 */
function getUrlVar(url, name){
	var r = getUrlVars(url)[name];
	if (r == undefined)
		return '';
	return r;
}

/**
 * Helper to get the default value of an input field
 */
function getDefaultValue(thisEl){
	return ($.trim(thisEl.attr("alt")) != '') ? $.trim(thisEl.attr("alt")) : $.trim(thisEl.attr("defaultValue"));
}

/**
 * Helper to setup text input on focus
 */
function smartFocus(){
	if (!$(this).hasClass("nochange")) {
		var defaultValue = getDefaultValue($(this));
		if (this.value == defaultValue) {
			this.value = '';
		}
		$(this).css({'font-style':'normal', 'letter-spacing':'0'});
	}
	$(this).addClass('focus');
}

/**
 * Helper to setup text input on blur
 */
function smartBlur(){
	if (!$(this).hasClass("nochange")) {
		var defaultValue = getDefaultValue($(this));
		if ($.trim(this.value) == '') {
			$(this).css({'font-style':'', 'letter-spacing':''});
			this.value = defaultValue;
		}
	}
	$(this).removeClass('focus');
}

/* ==================================================
                 S M A R T A L E R T
================================================== */

function triggerSmartAlert(msg){
	// TODO: function is just prototype
	alert(msg);
	return false;
}

function closeSmartAlert(){
	// TODO: function is just prototype
}

/* ==================================================
    	           M O D A L 
================================================== */

function triggerFocus(){ 
	if(modalState==0){
		$("#modal-focus").css({"opacity": "0.80"});
		$("#modal-focus").fadeIn("slow");
		modalState = 1;
	}
}

function closeModal(isChild, forceClose){
	if (isChild) {
		if (parent.modalState == 1) {
			if (forceClose || !parent.$(".modal.in-focus").hasClass("no-close")) {
				parent.$(".modal").fadeOut("slow");
				parent.$("#modal-focus").fadeOut("slow");
				parent.$(".modal").removeClass("in-focus");
				parent.modalState = 0;
			}
		}
	}
	else {
		if (modalState == 1) {
			if (forceClose || !$(".modal.in-focus").hasClass("no-close")) {
				$(".modal").fadeOut("slow");
				$("#modal-focus").fadeOut("slow");
				$(".modal").removeClass("in-focus");
				modalState = 0;
			}
		}
	}
}

function centerModal(){

	// relative positions affect absolute positioning, so lets find the closest 'relative' parent's offset
	var closestRelativeParent = $(".in-focus").parents().filter(function() { 
		// reduce to only relative position or "body" elements
		return $(this).is('body') || $(this).css('position') == 'relative';
	}).slice(0,1);
	var relativeOffset = closestRelativeParent.offset();

	var winH = $(window).height();
	var winW = $(window).width();
	var modalH = $(".in-focus").outerHeight();
	var modalW = $(".in-focus").outerWidth();
	var scrollTop = $(window).scrollTop() - relativeOffset.top;
	var scrollLeft = $(window).scrollLeft() - relativeOffset.left;

	var top = winH/2 - modalH/2 + scrollTop;
	if (top < scrollTop)
		top = scrollTop;

	var left = winW/2 - modalW/2 + scrollLeft;
	if (left < scrollLeft)
		left = scrollLeft;

	$(".in-focus").css({"position":"absolute", "top":top, "left":left});
}

/**
function EDITER_DIVIDER(){} ========================================================================
 */

/**
 * Get number of adoptions selected to buy
 */
function countAdoptionSelections(){
	var count = $(".department .option.selected").length;
	var countDisp = (count > 0) ? '(' + count + ')' : '';
	$("#page-footer .count").text(countDisp);

	return count;
}

/**
 * Get number of rental options selected to buy
 */
function countRentalSelections(){
	var count = $(".department .option.rental.selected").length;

	return count;
}

/**
 * Add cart summary and other display to rental requirements modal
 */
function updateRentalRequirementsModal(){

	// setup months to display
	//	- using full month names to match our inputs in billing
	var month = new Array(12);
	month[1] = "January";
	month[2] = "February";
	month[3] = "March";
	month[4] = "April";
	month[5] = "May";
	month[6] = "June";
	month[7] = "July";
	month[8] = "August";
	month[9] = "September";
	month[10] = "October";
	month[11] = "November";
	month[12] = "December";

	// initialize our expire date
	var maxMonth = 0;
	var maxYear = 0000;

	// clear cart
	$("#rental-requirements #cart-contents").html('');

	// add header row
	var newRow = '<tr><th class="item-title">Title</th><th class="item-price">Price</th></tr>';
	$("#rental-requirements #cart-contents").append(newRow);

	$(".department .option.rental.selected").each(function() {
		// find parent elements
		var thisDept = $(this).parents(".department");
		var thisCourse = $(this).parents(".course");
		var thisItem = $(this).parents(".item");
		var thisOption = $(this);

		// setup the max expire date
		//	- NOTE: if the displays ever change in the html, this will need to be updated to find the correct substring
		var expireMonth = parseInt(thisOption.find("li.term").text().substring(9, 11), 10);
		var expireYear = parseInt(thisOption.find("li.term").text().substring(15, 19), 10);
		if (expireYear > maxYear || (expireYear == maxYear && expireMonth > maxMonth)) {
			maxMonth = expireMonth;
			maxYear = expireYear;
		}

		// setup the cart row displays
		var section = '';
		if (thisDept.length && thisDept.find(".department-header h3").length)
			section += thisDept.find(".department-header h3").html();
		if (thisCourse.length && thisCourse.find(".course-header h3").length) {
			if (section != '')
				section += ' &#8226; ';
			section += thisCourse.find(".course-header h3").html();
		}
		if (section != '')
			section = '<div>' + section + '</div>';
		var title = thisItem.find("h4").html();
		var price = thisOption.find("li.price").text();
		var data = ''
		if (thisItem.find(".data li.required").length) {
			data = thisItem.find(".data li.required").html();
			if (data != '')
				data = '<ul class="data"><li class="required">' + data + '</li></ul>';
		}

		// add new row
		newRow = '<tbody><tr>';
		newRow += '<td class="item-title">' + section + '<a href="#" onClick="return false;">' + title + '</a>' + data + '</td>';
		newRow += '<td class="item-price">' + price + '</td>';
		newRow += '</tr></tbody>';
		$("#rental-requirements #cart-contents").append(newRow);
	});

	// update expire date
	$("#rental-requirements .rentalExpireDate").html(month[maxMonth] + " " + maxYear);

	// ReZebra the cart rows
	$('#rental-requirements #cart-contents tr').removeClass('alt');
	$('#rental-requirements #cart-contents tr:even').addClass('alt');
}

/**
 * Prepare cart actions
 */
function updateCartActions(){

	// CART - REMOVE ITEM
	// 2011-07-08 AZ: do not use ajax at this point, too many displays to update (voucher totals, error/warning messages)
//	$("#cart .item-remove a").live("click",function(event){
//		clickRemoveItem($(this), null);
//		return false;
//	});
}

/**
 * Handle removing an item from the cart
 */
function clickRemoveItem(thisEl, clickedEl){

	// optionally pass the clicked element, so we can properly show the loading image
	if (clickedEl === null)
		clickedEl = thisEl;

	// make ajax request
	$.ajax({
		type: 'POST',
		url: webservice,
		data: ({
			'ACTION':'remove',
			'FVNUM_1':getUrlVar(thisEl.attr("href"), "FVNUM_1"),
			'FVSEQ_1':getUrlVar(thisEl.attr("href"), "FVSEQ_1"),
			'FVBOM_1':getUrlVar(thisEl.attr("href"), "FVBOM_1"),
			'CSID':getUrlVar(thisEl.attr("href"), "CSID")
		}),
		dataType: 'xml',
		success: function(data) {
			commonSuccessDisplay(thisEl, data);
			successRemoveItem(thisEl, data);
		},
		error: commonErrorDisplay,
		loadingID: loadingID++,
		thisEl: clickedEl
	});
}

/**
 * Update displays upon successful item remove
 */
function successRemoveItem(thisEl, data){

	// return if we do NOT have a success flag
	// NOTE: should have all error messages handled from the response already
	if ($(data).find('SUCCESS').text() != 'true')
		return false;

	thisEl.parents("tbody").fadeOut(300, function(){

		// remove the parent row from the DOM
		$(this).remove();

		// update the buy button displays
		// the cart can have the same book/condition on multiple rows, see if still in cart first
		var buy_value = getUrlVar(thisEl.attr("href"), "FVCOND_1") + "_" + getUrlVar(thisEl.attr("href"), "FVNUM_1");
		var inCart = false;
		$("#cart .item-remove a").each(function() {
			var buy_value_check = getUrlVar($(this).attr("href"), "FVCOND_1") + "_" + getUrlVar($(this).attr("href"), "FVNUM_1");
			if (buy_value_check == buy_value)
				inCart = true;
		});
		if (inCart === false) {
			$(".pricing-matrix .option").each(function() {
				if ($(this).find("input[name='buyitem_0']").val() == buy_value) {
					$(this).removeClass("selected").removeClass("focus");
				}
			});
		}

		// update the cart displays
		updateCartDisplays();
	});
}

/**
 * Update the cart displays
 */
function updateCartDisplays(){

	// get the cart element
	var thisCart = $("#cart");

	// gether the cart stats
	var cartCount = 0;
	var cartSubtotal = 0.0;
	var cartRebate = 0.0;

	thisCart.find("#cart-contents tbody.item-row").each(function() {
		count = parseInt($(this).find(".item-quantity div").text(), 10);
		cartCount += count;
		cartSubtotal += (parseFloat($(this).find(".item-price").text().substring(1)) * count);
		if ($(this).find(".rebate-value").length)
			cartRebate += (parseFloat($(this).find(".rebate-value").text().substring(1)) * count);

	});

	// update the displays
	if (cartCount > 0) {
		// ReZebra the cart rows
		thisCart.find('#cart-contents tr').removeClass('alt');
		thisCart.find('#cart-contents tr:even').addClass('alt');

		thisCart.show();
		$("#page-description .cart-full").show();
		$("#page-description .cart-empty").hide();

		$("#subnav_mycart").html('My Cart <strong class="cart-count">' + cartCount + '</strong>');
		thisCart.find("h4 span").html(cartCount);
		thisCart.find(".current-total").html('<strong>Current Total</strong> ' + cartSubtotal.ppPriceMarkup());
		if (cartRebate > 0)
			thisCart.find("#cart-notes .buyback .buyback-value").html('$' + cartRebate.toFixed(2));
		else
			thisCart.find("#cart-notes .buyback").hide();

		if (thisCart.find(".new")) {
			var thisNew = thisCart.find(".new");
			multiToggleClass(thisNew, 'new', 7, 400, true);
		}
	}
	else {
		thisCart.hide();
		$("#page-description .cart-full").hide();
		$("#page-description .cart-empty").show();

		$("#subnav_mycart").html('My Cart');
	}
}

/**
 * Handle buy option click
 */
function clickBuyWidget(thisEl){

	var parentForm = thisEl.find("form");

	if (thisEl.hasClass('selected')) {
		// TODO: should we toggle and remove the item???
		return false;
	}

	// make ajax request
	$.ajax({
		type: 'POST',
		url: webservice,
		data: ({
			'ACTION':'buy',
			'from':parentForm.find("input[name='from']").val(),
			'bkn_0':parentForm.find("input[name='bkn_0']").val(),
			'coursekey_0':parentForm.find("input[name='coursekey_0']").val(),
			'title_0':parentForm.find("input[name='title_0']").val(),
			'buyitem_0':parentForm.find("input[name='buyitem_0']").val(),
			'CSID':getUrlVar(parentForm.attr("action"), "CSID")
		}),
		dataType: 'xml',
		success: function(data) {
			commonSuccessDisplay(thisEl, data);
			successBuyItem(thisEl, data);
		},
		error: commonErrorDisplay,
		loadingID: loadingID++,
		thisEl: thisEl
	});
}

/**
 * Update displays upon successful item add
 */
function successBuyItem(thisEl, data){

	// return if we do NOT have a success flag
	// NOTE: should have all error messages handled from the response already
	if ($(data).find('SUCCESS').text() != 'true')
		return false;

	// get new cart row
	var newRow = $($(data).find('R_CARTROW').text());
	newRow.find('tr').addClass('new');
	var thisLink = newRow.find(".item-remove a").attr("href");
	var thisCond = getUrlVar(thisLink, "FVCOND_1");
	var thisBook = getUrlVar(thisLink, "FVNUM_1");

	// add row to cart displays
	newRow.appendTo($("#cart #cart-contents"));

	// update the buy button displays
	var buy_value = thisCond + "_" + thisBook;
	$(".pricing-matrix .option").each(function() {
		if ($(this).find("input[name='buyitem_0']").val() == buy_value) {
			$(this).addClass("selected").addClass("focus");
		}
	});

	// update the cart displays
	updateCartDisplays();
}

/**
 * Helper to toggle a element's class for effects
 */
function multiToggleClass(el, className, count, delay, clearClass){
	count--;
	if (count < 0) {
		if (clearClass)
			el.removeClass(className);
		return;
	}

	el.toggleClass(className);

	setTimeout(function() {
		multiToggleClass(el, className, count, delay, clearClass)
	}, delay);
}

/**
 * Sign In
 */
function initializeSignInOptions(){

	// return if no sign in form
	if (!$("#sign-in").length)
		return;

	// fixup password input
	var pwClear = $("#fvLoginPasswordClear");
	var pwEncrypt = $("#fvLoginPassword");
	initializePasswordInputs(pwClear, pwEncrypt);

	// fixup the sign in submit action
	$("#sign-in").submit(function(){
		var thisEl = $(this);
		var emailInput = thisEl.find("input[name='fvEmailLogIn']");
		var emailDefault = getDefaultValue(emailInput);
		var passwordInput = thisEl.find("input[name='fvLoginPassword']");

		// client side validation
		if ($.trim(emailInput.val()) == '' || emailInput.val() == emailDefault)
			return triggerSmartAlert('Please enter your Email Address');
		else if (!$.isEmail(emailInput.val()))
			return triggerSmartAlert('Please enter a valid Email Address');

		if ($.trim(passwordInput.val()) == '')
			return triggerSmartAlert('Enter your Password');
		else if (!$.isVBPassword(passwordInput.val()))
			return triggerSmartAlert('Your Password must be 5-10 letters or digits');
	});

	// hide alert handlers
	$("#fvEmailLogIn, #fvLoginPasswordClear, #fvLoginPassword").mouseup(function(){
		closeSmartAlert();
	}).focus(function(){
		closeSmartAlert();
	});
}

/**
 * Init password inputs
 */
function initializePasswordInputs(pwClear, pwEncrypt){
	pwClear.show();
	pwEncrypt.hide();
	pwClear.focus(function(){
		pwClear.hide();
		pwEncrypt.show();
		pwEncrypt.focus();
	});
	pwEncrypt.blur(function(){
		if (pwEncrypt.val() == '') {
			pwEncrypt.hide();
			pwClear.show().removeClass('focus');
		}
	});
}

