/********************************/
/******* DECLARE CLASSES ********/
/********************************/


/**
 * StringUtil - utility class
 */
var StringUtil = new Class({

	/**
	 * Adds an item to a delimited text list. Note: the list cannot contain double quotes.
	 * Ex:	<code><input id="requiredFields" value="field1,field2"/>
	 *		<input type="button" onclick="global.stringUtil.addToDelimitedList('requiredFields', 'field3', 'value', ',')"/>
	 *		<input type="button" onclick="global.stringUtil.addToDelimitedList($('requiredFields'), 'field3')"/>
	 */
	addToDelimitedList : function(objOrId, item, attributeName, delim) {

		var obj = $(objOrId); // allow string or obj to be passed in
		attributeName = $defined(attributeName) ? attributeName : 'value'; // default attributeName

		var listText = eval('obj.' + attributeName); // get the text value of the list (e.g. 'value1,value2')

		if (listText.length == 0) { // necessary to avoid leading comma
			eval('obj.' + attributeName + ' = item');
		} else {
			var tokens = this.tokenize(listText, delim);
			tokens.include(item);
			eval('obj.' + attributeName + ' = "' + this.serialize(tokens, delim) + '"');
		}

	},

	/**
	 * Adds an item to a delimited text list. Note: the list cannot contain double quotes.
	 * Ex:	<code><input id="requiredFields" value="field1,field2"/>
	 *		<input type="button" onclick="global.stringUtil.removeFromDelimitedList('requiredFields', 'field3', 'value', ',')"/>
	 *		<input type="button" onclick="global.stringUtil.removeFromDelimitedList($('requiredFields'), 'field3')"/>
	 */
	removeFromDelimitedList : function(objOrId, item, attributeName, delim) {

		var obj = $(objOrId); // allow string or obj to be passed in
		attributeName = $defined(attributeName) ? attributeName : 'value'; // default attributeName

		if (!$defined(obj)) {
			global.log('obj not defined in : ' + obj);
		}

		var listText = eval('obj.' + attributeName); // get the text value of the list (e.g. 'value1,value2')

		var tokens = this.tokenize(listText, delim);
		tokens.remove(item);
		eval('obj.' + attributeName + ' = "' + this.serialize(tokens, delim) + '"');

	},

	/**
	 * Checks to see if the str is in the list
	 */
	isInDelimitedList : function (list, item) {
		return this.serialize(list).contains(item);
	},

	/**
	 * Tokenize a string. Returns an array of each item. A zero-length Array if null/undefined/'' is passed in,
	 * otherwise an Array of length >= 1. Defaults to , as the delimiter. Can use a str as delimiter
	 */
	tokenize : function (str, delim) {

		delim = $defined(delim) ? delim : ','; // default delimiter
		var tokens = new Array();

		if (str != null || str == '' || !$defined(str)) { // contains zero items

			var tempstr = str;

			while (tempstr.contains(delim)) {

				var token = tempstr.substring(0, tempstr.indexOf(delim));
				tokens.include(token);
				tempstr = tempstr.substring(tempstr.indexOf(delim) + 1, tempstr.length); // trim this element off of the string

			}

			tokens.include(tempstr);
		}

		return tokens;
	},

	/**
	 * Take an array and return it as a comma-delimited string. Comma is default delimiter. Returns null as an empty string.
	 */
	serialize : function (tokens, delim) {

		delim = $defined(delim) ? delim : ','; // default delimiter

		var str = '';

		if (tokens != null && tokens.length > 0) {

			tokens.each(function(item) {
				str += item + ',';
			});

			str = str.substring(0, str.length - 1); // trim trailing comma
		}

		return str;
	}

});

/**
 * A uclass to contain global functionality and utilities
 */
var Global = new Class({

	options : {
		DEBUG_LEVEL : 5 // 5=TRACE, 4=DEBUG, 3=INFO, 2=WARN, 1=ERROR
	},

	isSafari : window.webkit, // safari or konqueror
	isSafari2down : window.webkit419, // safari 2 and under
	isSafari3up : window.webkit420, // safari 3 and up
	isGecko : window.gecko, // gecko browsers

	lastLog : new Date(), // last log timestamp

	stringUtil : new StringUtil(),

	profileRetrieverObjects : new Array(),

	initialize : function(options) {
		this.setOptions(options);
	},

	/*
	 * Browser bugfix.  Jogs the height of the element in order to trigger a redraw (use in places where you manipulate the dom and other elements don't respond as expected (like the invite map blurb on the college center)
	 */
	jogHeight : function (elementId) {

		if ($(elementId)) {
			(function () {
				if ($(elementId).getStyle('height') != '1%') {
					$(elementId).setStyle('height', '1%');
				} else {
					$(elementId).setStyle('height', '2%');
				}
			}).delay(10);
		}
	},

	/*
	 * Sends a message to window.console if exists, or inside debugLog if exists, or at the end of the body element (hidden with jsHide).
	 * Displays hh:mm:ss timestamp and ms since last log message.
	 */
	log : function (text) {

		var now = new Date();
		var msSinceLastLog = now.getTime() - global.lastLog.getTime();
		var timestamp = (now.getHours() < 10 ? '0' : '') + now.getHours() + ':' +
					 (now.getMinutes() < 10 ? '0' : '') + now.getMinutes() + ':' +
					 (now.getSeconds() < 10 ? '0' : '') + now.getSeconds();
		global.lastLog = now;


		text =  timestamp + ' (' + msSinceLastLog + "): " + text;

		if (window.console) {
			window.console.log(text);

		} else {
			var logEntryElem = new Element('p', {'class' : 'debugLogEntry'});
			logEntryElem.setText(text);
			if ($('debugLogTarget')) {
				logEntryElem.injectInside($('debugLogTarget'));
				// DISABLING LOGGING IN IE PROD			} else {
				//				logEntryElem.addClass('jsHide');
				//				logEntryElem.injectInside($E('body'));
			}
		}
	},


	error : function (text) {

		if (this.options.DEBUG_LEVEL >= 1) {
			this.log(text);
		}
	},

	warn : function (text) {

		if (this.options.DEBUG_LEVEL >= 2) {
			this.log(text);
		}
	},

	info : function (text) {

		if (this.options.DEBUG_LEVEL >= 3) {
			this.log(text);
		}
	},

	debug : function (text) {

		if (this.options.DEBUG_LEVEL >= 4) {
			this.log(text);
		}
	},

	trace : function (text) {

		if (this.options.DEBUG_LEVEL >= 5) {
			this.log(text);
		}
	},

	tryPreDomready : function (funcToCall) {
		try {
			funcToCall();
		} catch (e) {
			window.addEvent('domready', funcToCall);
		}
	},

	assert : function (truth, errorMsg) {

		if (!truth) {
			global.debug('Assertion failed:  (' + errorMsg + ')');
			throw new Error('Assertion failed:  (' + errorMsg + ')');
		}
	},

	/**
	 * USAGE: 	try {
	 *				// ...
	 *			} catch (e) {
	 *				global.handleError(e);
	 *			}
	 * COMMON ERRORS:
	 *    *  ECMAScript exceptions:
	 *        o Error
   	 *	      o EvalError
   	 *    	  o RangeError
	 *        o ReferenceError
     *	      o SyntaxError
     *  	  o TypeError
	 *        o URIError
     *	* DOM exceptions:
     *		  o DOMException
	 *        o EventException
     *	      o RangeException
     *  	  o ... (?)
	 *  * nsIXPCException (XPConnect)
	 */
	handleError : function (e) {
		global.log("JS Error - Name: " + e.name + " Msg: " + e.message);
	},

	/* not currently used on site - khastie 2009-02-18 */
	/* must have fixpng.js included on page */
	redoFixPNG : function () {
			if(window.ie6) {
				$$('img.fixPNG').each(function(png){
				fixPNG(png);
			});
		}
	},

	initButtonRollovers : function() {
		
		$$('.submitButton').addEvent('mouseover', function() {
			this.addClass('active');
		});
		$$('.submitButton').addEvent('mouseout', function() {
			this.removeClass('active');
		});

		if (window.ie6) {
			$$('.buttonLink').addEvent('mouseover', function() {
				this.addClass('buttonLinkActive');
			});

			$$('.smallButtonLink').addEvent('mouseover', function() {
				this.addClass('buttonLinkActive');
			});

			$$('.buttonLink').addEvent('mouseout', function() {
				this.removeClass('buttonLinkActive');
			});

			$$('.smallButtonLink').addEvent('mouseout', function() {
				this.removeClass('buttonLinkActive');
			});
		}

	},

	/**
	 * Work around IE only firing onchange events when focus is lost. IE is actually right on this one, according to HTML 4 specs, but it's a pain.
	 */
	addOnChangeEventBrowserSafe : function(elemOrID, theFunction) {

		var elem = $(elemOrID);

		if (window.ie) {
			elem.addEvent('click', theFunction);
			elem.addEvent('keyup', theFunction);
		} else {
			elem.addEvent('change', theFunction);
		}

	}

});

Global.implement(new Options);

/*******************************/
/******* EXTEND MOOTOOLS *******/
/*******************************/

/* The following functions are meant to bypass some of the slower mootools calls, especially when doing effects. */

Element.extend({

	getWidth : function () {
		return this.offsetWidth;
	},

	getHeight : function () {
		return this.offsetHeight;
	},

	setWidth : function (sizeIncludingPx) {
		this.style['width'] = sizeIncludingPx;
	},

	setHeight : function (sizeIncludingPx) {
		this.style['height'] = sizeIncludingPx;
	},

	getPagePosition : function () {

		var currentElem = this;
		var posY = 0;
		var posX = 0;

		while (currentElem != null) {
			posY += currentElem.offsetTop;
			posX += currentElem.offsetLeft;
			currentElem = currentElem.offsetParent;
		}

		return {
			x : posX,
			y : posY,
			width : this.getWidth(),
			height: this.getHeight()

		};
	},

	debugPosition : function () {

		global.debug(
			'this.style.top: ' + this.style.top +
			'\nthis.style.left: ' + this.style.left +
			'\nthis.style.width: ' + this.style.width +
			'\nthis.style.height: ' + this.style.height +
			'\nthis.offsetTop: ' + this.offsetTop +
			'\nthis.offsetLeft: ' + this.offsetLeft +
			'\nthis.offsetWidth: ' + this.offsetWidth +
			'\nthis.offsetHeight: ' + this.offsetHeight +
			'\nthis.getPagePosition: ' + this.getPagePosition().x + ', ' + this.getPagePosition().y

		);
	}
});

/********************************/
/***** DECLARE GLOBAL VARS ******/
/********************************/

var global = new Global({DEBUG_LEVEL : 1});

/********************************/
/******** EVENT HANDLERS ********/
/********************************/

// browser-specific: adds a body class to target safari
window.addEvent('domready', function() {
	if (global.isSafari2down) {
		$$('body').addClass('safari2');
	};
	if (global.isSafari3up) {
		$$('body').addClass('safari3');
	};
	if (global.isSafari) {
		$$('body').addClass('safari');
	};
	if (global.isGecko) {
		$$('body').addClass('gecko');
	}
 
});

/********************************/
/********** FUNCTIONS ***********/
/********************************/

function initBody() {
	// default function to be overridden by includes.  BE SURE GLOBAL.JS IS THE FIRST .JS FILE INCLUDED
}

/***************************/
/********** Forms **********/
/***************************/

/**
 * Highlights the specified form field if its value is equal the specified string
 */
function autoSelect(field, value) {
	if (field.value == value) field.select();
}

/**
 * Redirects the current page to the url specified by the value of the currently selected SELECT (dropdown) OPTION.
 * Usage: <select onclick="redirectDropdown(this)" ...
 * @param el the SELECT element
 */
function redirectDropdown(el) {
	window.location.href = el.options[el.selectedIndex].value;
}

/***************************/
/********** Popups *********/
/***************************/

/**
 * Opens an ajaxified stickyWin, creating an Pass in false or '' for params you want to leave as default.
 *
 * @param ajaxUrl the url of the ajax call
 * @param ajaxTargetId id of the element to load the ajax results into (usually the id of the _content_ div)
 * @param zIndex
 * @param height
 * @param width
 * @param fadeDuration
 * @param backgroundColor
 * @param opacity
 * @return a reference to the StickyWinFxModal object
 **/
function openAjaxStickyWin(ajaxUrl, ajaxTargetId, zIndex, height, width, fadeDuration, backgroundColor, opacity, elementsToHide) {

	var content = '<div id="' + ajaxTargetId + '"></div>';

	new AjaxCpx(ajaxUrl, {

		update: ajaxTargetId,

		onSuccess: function() {

			global.initButtonRollovers();
			
			if (typeof formController != 'undefined') { // double check that form controller exists before calling this
				formController.initDisableOnSubmit();
			}
		}

	}).request();

	return openAjaxSupportStickyWin(content, zIndex, height, width, fadeDuration, backgroundColor, opacity, elementsToHide);
}

/**
 * Opens a sticky window given specific content and sets some events related to closing/reopening. Pass in false or '' for params you want to leave as default
 *
 * @param content an HTML string or an HTML elem
 * @return a reference to the StickyWinFxModal object
 */
function openAjaxSupportStickyWin(content, zIndex, height, width, fadeDuration, backgroundColor, opacity, elementsToHide) {

	if (!content) content = '';
	if (!width) width = 400;
	if (!height) height = false;
	if (!fadeDuration) fadeDuration = 200;
	if (!zIndex) zIndex = 10000;
	if (!backgroundColor) backgroundColor = '#000';
	if (!opacity) opacity = 0.5;
	if (!elementsToHide) elementsToHide = 'select';

	var theStickyWin = new StickyWinFxModal({
		content: content,
		width: width,
		height: height,
		fadeDuration: fadeDuration,
		zIndex: zIndex,
		modalOptions: {
			elementsToHide : elementsToHide,
			modalStyle: {
				'background-color': backgroundColor,
				'opacity': opacity
			}
		}});

	/**
	 * Remove the stickyWin and its modalOverlay from the DOM, rather than just hiding them. Allows the stickyWin to be reopened (ticket #528).
	 * Add as an onclick to closeSticky buttons, etc....
	 */
	theStickyWin.kill = function () {
		this.modalHide();
		this.win.remove();
		$('modalOverlay').remove();
		if ($('StickyWinShim_shim')) {
			$('StickyWinShim_shim').remove();
		}

		/*
		* NOTE: This check is to see if the light box is on the college dashboard.
		*       If it is, then refresh the page after the light box is closed,
		*		thus updating any fields that were added/deleted with one of the lighboxes.
		*/
		if ($('addStudentQuestionEmailStickyLink')) {
			window.location.reload();
		}
	};

	// NB: to prevent clicking on the shim from closing the lightbox, add this line: $('modalOverlay').removeEvents('click');

	// add kill event to the modalOverlay
	$('modalOverlay').addEvent('click', function() {
		this.kill();
	}.bind(theStickyWin));

	/* the following does not work, so event is hardcoded
	// add kill event to "closeSticky"-classed elements
	theStickyWin.win.getElements('.' + theStickyWin.options.closeClassName).each(function(el) {
		el.addEvent('click', this.kill.bind(this));
	}, theStickyWin);
	*/

	return theStickyWin;
}

/**
 * Opens a sticky window given specific content. Pass in false or '' for params you want to leave as default
 *
 * @param content an HTML string or an HTML elem
 * @return a reference to the StickyWinFxModal object
 * @example This example allows for a lightbox to have events applied to the buttons inside it. It remains on the page, and instead of being created over and over ever time it's opened, it is created once, then shown/hidden
 *
		if (this.sessionCheckerStickyWin == null) {
	        this.sessionCheckerStickyWin = openStickyWin('<div id="fooAlert" class="alertBox"><div class="alertBoxBottomRight"> <div class="alertBoxTopLeft"> <div class="alertBoxTopRight"> <a class="closeSticky" href="javascript:void(0)"> <img src="/img/icons/close.gif" alt="close"/> </a> blah </div> </div> </div> </div> </div>', 99000);
		} else {
			this.sessionCheckerStickyWin.show();
		}

 */
function openStickyWin(content, zIndex, height, width, fadeDuration, backgroundColor, opacity, elementsToHide) {

	if (!content) content = '';
	if (!width) width = 400;
	if (!height) height = false;
	if (!fadeDuration) fadeDuration = 600;
	if (!zIndex) zIndex = 10000;
	if (!backgroundColor) backgroundColor = '#000';
	if (!opacity) opacity = 0.5;
	if (!elementsToHide) elementsToHide = 'select';

	return new StickyWinFxModal({
		content: content,
		width: width,
		height: height,
		fadeDuration: fadeDuration,
		zIndex: zIndex,
		modalOptions: {
			elementsToHide : elementsToHide,
			modalStyle: {
				'background-color': backgroundColor,
				'opacity': opacity
			}
		}
	});
}

window.addEvent('domready', function() {
	$$('.largeGB').addEvent('click', function() {
		$E('body').addClass('largeGBStyles');
	});
	$$('.regularGB').addEvent('click', function() {
		$E('body').removeClass('largeGBStyles');
	});
});

/***************************/
/**** Button Rollovers *****/
/***************************/

window.addEvent('domready', global.initButtonRollovers.pass('body'));

/***************************/
/********* Tooltips ********/
/***************************/

window.addEvent('domready', function() {
	var myTips = new Tips($$('.toolTips'), {
		maxTitleChars: 50,
		showDelay: 333
	});
});

/***************************/
/**** Referrer Tracking ****/
/***************************/

window.addEvent('domready', function() {
	var codeParam = getURLParam("code");
	if (codeParam != "none") {
		document.cookie = "cpxRefCode" + "=" + escape(codeParam) + ";path=/";
	}
});

/**
 * Used to find the code= param from the url.
 */
function getURLParam(strParamName, overrideURL) {
	var strReturn = "none";
	var strHref = (overrideURL ? overrideURL : window.location.href);
	if (strHref.indexOf("?") > -1) {
		var strQueryString = strHref.substr(strHref.indexOf("?") + 1);
		var aQueryString = strQueryString.split("&");
		for (var iParam = 0; iParam < aQueryString.length; iParam++) {
			if (aQueryString[iParam].toLowerCase().indexOf(strParamName.toLowerCase() + "=") > -1) {
				var aParam = aQueryString[iParam].split("=");
				strReturn = aParam[1];
				break;
			}
		}
	}
	return unescape(strReturn);
}
