/**
 * This is our own utility JavaScript that will be available on all pages.
 */
 
/**
 * ffl is our hash of constants that we need throughout the site
 */
var ffl = {
	usePrototypeWindow: false,
	
	tinyMceDefaults: {
		mode: "exact",
		theme: "advanced",
		convert_urls: false,
		theme_advanced_toolbar_align: "left",
		theme_advanced_buttons1: "fontselect,fontsizeselect,bold,italic,underline,justifyleft,justifycenter,justifyright,justifyfull,bullist,numlist,outdent,indent,charmap",
		theme_advanced_buttons2: "",
		theme_advanced_buttons3: ""
	},
	
	tinyMceMetaDefaults: {
		mode: "exact",
		theme: "advanced",
		convert_urls: false,
		theme_advanced_toolbar_align: "left",
		nowrap: true,
		theme_advanced_buttons1: "charmap",
    	theme_advanced_buttons2: "",
	    theme_advanced_buttons3: "",
		height: 70,
		width: 600,
		valid_elements: "x", //prevents all html tags
		remove_linebreaks : true
	},
	
	/*
		there is a bug in prototype 1.6.0.2 (see ticket 11182) that affects clonePosition and
		essentially causes scriptaculous autocompleters to not display matching
		results the first time the autocomplete is invoked. This only occurs in IE. Essentially
		the best I could come up with is to duplicate the offending code in the autoupdater
		that fails the first time it is called and invoke it. That way when the autoupdater eventually
		is used, the first-time crappyness has already passed.
	*/
	fixAutoCompleter: function(element) {		

		if (Prototype.Browser.IE)
		{
			element = $(element);
			
			//execute the bad code and swallow the error
			try 
			{
				$(element.id + "_autoComplete").clonePosition(element, {
					setHeight: false, 
					offsetTop: element.offsetHeight
				});
			} catch (e) {}
		}
	}
}
 
//string extensions
Object.extend(String.prototype, {
		
	isNumeric: function() {
		//blank strings are always considered valid
		return this == "" || /^-?([0-9]+(\.[0-9]+){0,1}|\.[0-9]+)$/.test(this);
	},
	
	isEmail: function() {
		//blank strings are always considered valid
		return this == "" || /^([a-zA-Z0-9_\.\-])+\@([a-zA-Z0-9.\-])+(\.[a-zA-Z0-9]{2,4})+$/.test(this);
	},
	
	//used to encode a string for use in a regular expression.
	//this is useful for the string.replace method, since the first
	//argument normally takes a RegExp object.
	regexEncode: function() {
		return this.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
	},

	createDate: function(){
		var dateParts = this.split("/");
		var month = parseInt(dateParts[0], 10);
		var day = parseInt(dateParts[1], 10);
		var year = parseInt(dateParts[2], 10);
		
		return new Date(year, month - 1, day, 0, 0, 0, 1);
	},
	
	isDate: function()
	{	
		//blank strings are always considered valid
		if (this == "")
		{
			return true;
		}
		
		//mm/dd/yyyy
		var pattern = /^(1[0-2]|0?[1-9])\/(0?[1-9]|[12][0-9]|3[01])\/(\d\d\d\d)$/;
		
		if (!pattern.test(this))
		{
			return false;
		}
		
		var dateParts = this.split("/");
		var month = parseInt(dateParts[0], 10);
		var day = parseInt(dateParts[1], 10);
		var year = parseInt(dateParts[2], 10);
		
		if (!IsDateValid(month, day, year))
		{
			return false;
		}
		
		return true;
	}
});

//date extensions
Object.extend(Date.prototype, {
	daysBetween: function (dateToCompare) {
	    var difference =
    	    Date.UTC(dateToCompare.getFullYear(), dateToCompare.getMonth(), dateToCompare.getDate(), 0, 0, 0)
	      - Date.UTC(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
	  
	    return Math.floor(Math.abs(difference / 1000 / 60 / 60 / 24));
	},
	toMySqlDateString: function(){
		return this.getFullYear().toString() + "-" + (this.getMonth() + 1).toString() + '-' + this.getDate().toString();
	},
	
	toShortDateString: function() {
		return (this.getMonth() + 1).toString() + "/" + this.getDate().toString()  + "/" + this.getFullYear().toString();
	},
	
	toLongDateString: function() {
		month = (this.getMonth() < 10) ? "0" + (this.getMonth() + 1).toString() : (this.getMonth() + 1).toString() ;
		day = (this.getDate() < 10) ? "0" + this.getDate().toString() : this.getDate().toString();
		return month + "/" + day + "/" + this.getFullYear().toString();
	},
	
	isWeekend: function() {
		return this.getDay() == 0 || this.getDay() == 6;
	},
	
	add: function(value, unit) {
		
		unit = unit.toLowerCase();
		
		switch (unit)
		{
			case "y":
				return new Date(this.getFullYear() + value, this.getMonth(), this.getDate(), this.getHours(), this.getMinutes(), this.getSeconds());
			case "m":
				return new Date(this.getFullYear(), this.getMonth() + value, this.getDate(), this.getHours(), this.getMinutes(), this.getSeconds());
			case "d":
				return new Date(this.getFullYear(), this.getMonth(), this.getDate() + value, this.getHours(), this.getMinutes(), this.getSeconds());			
			case "h":
				return new Date(this.getFullYear(), this.getMonth(), this.getDate(), this.getHours() + value, this.getMinutes(), this.getSeconds());
			case "n":
				return new Date(this.getFullYear(), this.getMonth(), this.getDate(), this.getHours(), this.getMinutes() + value, this.getSeconds());
			case "s":
				return new Date(this.getFullYear(), this.getMonth(), this.getDate(), this.getHours(), this.getMinutes(), this.getSeconds() + value);
			default:
				return null;
		}
	}
});

Object.extend(String.prototype, {
	numberFormat: function() {
		var parts = this.toString().split(".");
		var number = "";
		
		for (var i = parts[0].length - 1; i >= 0; i--)
		{		
			if ((parts[0].length - i) % 3 == 0 && i > 0)
			{
				number =  "," + parts[0].charAt(i) + number;
			}
			else
			{
				number = parts[0].charAt(i) + number;
			}
		}
		
		if (parts.length > 1)
		{
			number += "." + parts[1];
		}
		
		return number;
	}
});

function IsDateValid(month, day, year)
{
	if (month.toString().blank() 
		|| day.toString().blank() 
		|| year.toString().blank()
		|| !month.toString().isNumeric() 
		|| !day.toString().isNumeric() 
		|| !year.toString().isNumeric() 
		|| month.toString().indexOf(".") != -1
		|| day.toString().indexOf(".") != -1
		|| year.toString().indexOf(".") != -1
		|| year.toString().length != 4)
	{
		return false;
	}
	
	var date = new Date(year, month - 1, day, 0, 0, 0, 1);
	return date.getMonth() + 1 == month && date.getDate() == day && date.getFullYear() == year;
}

//alert dialog support
function alertDialog(text, okCallback) {
		
	if (ffl.usePrototypeWindow)
	{
		Dialog.alert(text, {
			onOk: okCallback || false, 
			okLabel: "OK", 
			windowParameters: {
				className: "alphacube", 
				effectOptions: {
					duration: .25
				} 
			} 
		}); 
		
		//focus on the "ok" button when the dialog finally appears
		var button = $$(".ok_button")[0];
		
		new PeriodicalExecuter(function(exec) { 
			if (button.visible() && button.up(".dialog").visible()) {
				button.focus();
				exec.stop();
			} 
		}, .1);
	}
	else
	{
		alert(text);
		okCallback();
	}
};

function contentDialog(element, options)
{
	element = $(element);
	
	//we preload all the alphacube images so FireFox can cope better
	//when a page is posted immediately after showing the content dialog
	var preloads = $A(['bottom-left-c.gif', 'bottom-middle.gif', 'bottom-right-c.gif', 'button-close-focus.gif', 'button-max-focus.gif', 'button-min-focus.gif', 'frame-left.gif', 'frame-right.gif', 'left-top.gif', 'right-top.gif', 'top-middle.gif']);
	
	preloads.each(function(preload) {
		var img = new Image(100, 100);
		img.src = '/css/Window/alphacube/' + preload;
	});
	
	options = Object.extend({
		className: "alphacube",
		effectOptions: {
				duration: .25 
		},
		destroyOnClose: true,
		modal: false
	}, options || {});
	
	var w = new Window("win", options);	
	w.setContent(element, true, true);
	
	if (options.width)
	{
		w.setSize(options.width, w.getSize().height);
	}
	
	if (options.height)
	{
		w.setSize(w.getSize().width, options.height);
	}
	
	w.toFront(); 
	w.showCenter(options.modal); 
	
	return w;
}

function confirmDialog(text, okCallback, cancelCallback, okLabel, cancelLabel, windowParameters) {
	Dialog.confirm(text, {
		onOk: okCallback,
		okLabel: okLabel || "OK",
		cancelLabel: cancelLabel || "Cancel",
		onCancel: cancelCallback, 
		windowParameters: Object.extend({
			className: "alphacube",
			effectOptions: {
				duration: .25 
			}
		}, windowParameters || {})
	});
}
/* validation methods - syntax modeled after prototype.js */
var Validation = {
	required: function(element) {
		element = $(element);
		
		if (($F(element) || "").blank()) {
			Validation.showError(element, arguments[1], arguments[2] || "is required.", arguments[3]);
			return false;
		}
		
		return true;
	},
	
	numeric: function(element) {
		element = $(element);
		
		if (!($F(element) || "").isNumeric()) {
			Validation.showError(element, arguments[1], arguments[2] || "must be a number.", arguments[3]);
			return false;
		}
		
		return true;
	},
	
	email: function(element) {
		element = $(element);
		
		if (!($F(element) || "").isEmail()) {
			Validation.showError(element, arguments[1], arguments[2] || "must be a valid email.", arguments[3]);
			return false;
		}
		
		return true;
	},
	
	date: function(element) {
		element = $(element);
		
		if (!($F(element) || "").isDate()) {
			Validation.showError(element, arguments[1], arguments[2] || "is not a valid date.", arguments[3]);
			return false;
		}
		
		return true;
	},
	
	pattern: function(element, regex) {
		element = $(element);
		
		if (!regex.test(($F(element) || ""))) {
			Validation.showError(element, arguments[2], arguments[3] || "is not valid.", arguments[4]);
			return false;
		}
		
		return true;
	},
	
	showError: function(element, name, predicate, callback) {
		
		if (!name) {
			var label = element.previous();
			
			if (label && label.tagName && label.tagName.toLowerCase() == "label") {
				name = label.innerHTML;
			}
		}
		
		if (name) {
			
			realCallback = null;
			
			internalCallback = function() {
				if (element.visible() && element.style.visibility != "hidden") {
					element.activate();
				}
				
				return true;
			};
			
			if (callback)
			{
				realCallback = function(w) { 
					callback(); 
					internalCallback();
					return true;
				};
			}
			else
			{
				realCallback = internalCallback;
			}
			
			alertDialog("The \"" + name + "\" field " + predicate, realCallback);
		}
	}
}

//binds a date picker to a form element
//requires /Style/fastDatePicker.css and /JavaScript/fastDatePicker.js
function bindDatePicker(elementName) {
	
		var element = $(elementName);
		
		var options = Object.extend({
			onSelect: Prototype.emptyFunction
		}, arguments[1] || {});
		
		var a = Element.extend(document.createElement("a"));
		a.href = "#";
		a.update("<img src=\"/img/calendar.png\" id='" + elementName + "_img' />");
		
		

		Event.observe(a, "click", function(evt) {
			var existingCalendar = $(element.id + "_cal");
						
			//if we already have the calendar showing, hide it
			if (existingCalendar)
			{
				existingCalendar.remove();
				return;
			}
	
			//create the div to hold the calendar
			var container = Element.extend(document.createElement("div"));
			var position = Position.cumulativeOffset(element);
			var dimensions = element.getDimensions();
			
			container.id = element.id + "_cal";
			
			//set up the position of the div
			container.setStyle({
				position: "absolute",
				backgroundColor: "#FFFFFF",
				left: position[0] + "px",
				top: (position[1] + dimensions.height) + "px"
			});
			
			//create the date picker
			var picker = new FastDatePicker();
			
			//wire it up so that when a date is picked, the element we're binding 
			//to gets the date in mm/dd/yyyy format
			picker.handleSelection = function dr_SelectDate(container, picker) {
				// original
				this.value = picker.date.toMySqlDateString();
				container.remove();
				options.onSelect(this);
			}.bind(element, container, picker);
			
			//don't put a restriction on the dates they can pick
			picker.firstSelectableDate = null;
			
			//if the field already has a date in it, let's tell the date picker
			//to default to that date
			if (!element.value.blank() && element.value.isDate())
			{
				picker.date = new Date(Date.parse(element.value));
				picker.selectedDate = new Date(picker.date);
			}
			
			//add the picker and container to the document
			container.appendChild(picker.calendar());
			element.up().appendChild(container);
			
			Event.stop(evt);
		});
		
		//add the a tag after the element
		if (!element.nextSibling)
		{
			element.up().appendChild(a);
		}
		else
		{
			element.up().insertBefore(a, element.nextSibling);
		}
};
/* shortcuts */
var $$R = Validation.required;
var $$N = Validation.numeric;
var $$E = Validation.email;
var $$D = Validation.date;
var $$P = Validation.pattern;