jQuery(document).ready(function() {
	jQuery('.hidden_until_ready').show().css('display', 'block');
	jQuery('.hide_until_ready').show().css('display', 'block');
});


/**
 * Escapes HTML content found in a string or an array of strings.
 * @param string The string or array of strings to modify
 * @return A safe string that can be embedded in HTML content
 */
jQuery.escape = function(string) {
	if (jQuery.isArray(string)) {
		for(var i=0; i<string.length; i++)
			string[i] = jQuery.escape(string[i]);
		return string;
	}
	else {
		return jQuery.trim(string)
			.replace(/&/g, '&amp;')
			.replace(/</, '&lt;')
			.replace(/>/, '&gt;')
			.replace(/"/, '&quot;')
			.replace(/'/, '&#39;');
	}
};

/**
 * Replaces whitespaces characters with '&nbsp;'.
 * @param string The string to fix.
 * @return The modified string.
 */
jQuery.whitespace = function(string) {
	return jQuery.trim(string).
		replace(/\W+/, '&nbsp;');
}

/**
 * Removes HTML tags from a string.
 * @param string The string to modify
 * @return A clean string
 */
jQuery.strip = function(string, options) {
	options = jQuery.extend({
		remove_style_bodies: true,
		remove_script_bodies: true
	}, options);

	if (jQuery.isArray(string)) {
		for(var i=0; i<string.length; i++)
			string[i] = $.strip(string[i]);
		return string;
	}
	else {
		return jQuery.trim(string)
			.replace(/<style[^>]*>[^<]+<\/style>/, '')
			.replace(/<script[^>]*>[^<]+<\/script>/, '')
			.replace(/<\/?[^>]+>/gm, '')
			.replace(/(&nbsp;)+/g, ' ');
	}
	
};

jQuery.stayPut = function(options) {
	
	clearTimeout(this.stayput_fade_timeout);
	
	if (!options) {
		if (this.stayput_dialog)
			this.stayput_dialog.hide();
		return true;
	}
	
	if (!options.message)
		options = { message: options };
	
	options = jQuery.extend({
		message: 'Nothing to report.',
		ceiling: (document.stayput) ? ((document.stayput.ceiling) ? document.stayput.ceiling : 0) : 0,
		fadeAfter: (document.fadeAfter) ? ((document.stayput.fadeAfter) ? document.stayput.fadeAfter : 0) : 0
	}, options);

	if (!this.stayput_dialog) {
		html = '<div class="stayput fixed"></div>';
		$('body').append(html);
		this.stayput_dialog = $('div.stayput');
		this.stayput_dialog
			.css('position', 'absolute')
			.css('top', 0);
	}

	var scrollTop = jQuery.viewport.getScrollOffsets().top;

	this.stayput_dialog
		.text(options.message)
		.css('top', (scrollTop > options.ceiling) ? scrollTop : options.ceiling)
		.css('left', (jQuery.viewport.getDimensions().width - this.stayput_dialog.width())/2)
		.fadeIn('slow');
		
	if (options.fadeAfter > 0) {
		this.stayput_fade_timeout = setTimeout( function() {
			jQuery.stayput_dialog.fadeOut('slow');
		}, options.fadeAfter * 1000 );
	}
	
};

jQuery.fn.disableButtons = function() {
	this.each(function() {
		for (var b=0; b<this.elements.length; b++) {
			var e = this.elements[b];
			if (e.type == 'button' || e.type == 'submit')
				e.disabled = true;
		}
	});
};

jQuery.fn.enableButtons = function() {
	this.each(function() {
		for (var b=0; b<this.elements.length; b++) {
			var e = this.elements[b];
			if (e.type == 'button' || e.type == 'submit')
				e.disabled = false;
		}
	});
};

/**
 * @param url The path to the form validation AJAX resource; must exist on
 * the same domain as the page that served the form
 * @param options
 */ 
jQuery.fn.validateAndSubmit = function(url, options) {
	// this is a form
	var form = this;
	
	options = jQuery.extend({
		alert_box_class: 'widealert',
		success: null,
		before_submit: null,
		error: null,
		post_with_ajax: true,
		use_stayput: true,
		use_impromptu: true,
		reset: false,
		inline_login_url: '/login/login?inline=1'
	}, options);
	
	var alert_box = $(options.alert_box_class);
	
	/** Alerts the user of an error iff there is an error to report. */
	var alert_user = function(status) {
		if (!status.hasErrors) {
			if (alert_box.length) {
				alert_box.addClass('valid');
				alert_box.text("It's all good now.");
			}
		}
		else {
			$.stayPut(false);
			var message = status.errors[0];
			if (alert_box.length) {
				alert_box.html(message);
				alert_box.removeClass('valid');
				alert_box.fadeIn('slow').flash();
			}
			else {
				if (options.use_impromptu)
					$.prompt(message);
				else
					alert(message);
			}
		}
	};

	form.append('<input type="hidden" id="__hidden_button" name="__button" value="" />');
	
	// assign click handlers to all of the form's buttons
	for(var i=0; i<form[0].elements.length; i++) {
		var e = form[0].elements[i];
		if (e.type == 'submit' && e.name == '__button') {
			e._other_onclick = e.onclick;
			e.onclick = function(evt) {
				if (!evt) evt = window.event;
				var src = (evt.srcElement) ? evt.srcElement : evt.target;
				$('#__hidden_button').val(src.value);
				if (src._other_onclick)
					src._other_onclick(evt);
			};
			
		}
	}

	this.submit(function() {
		
		if (!form[0].validation_disabled) {
			if (options.use_stayput)
				$.stayPut('Validating...');
		
			form.disableButtons();
		
			// first, do the validation with an ajax call
			$.ajax({
				type: 'POST',
				url: url,
				data: form.serializeArray(), 
				// if the ajax call succeeds, notify the user or submit the form
				success: function(status) {
					// status must be json - see function doc
					status = eval('('+status+')');
					
					if (!status.isAuthorized) {
						form.enableButtons();
						$.stayPut(false); // clear any lingering message...
						$.openThickBoxModal(options.inline_login_url);
						return;
					}
					
					else {
						alert_user(status);
						// can't submit the form if it has errors
						var submit_form = !status.hasErrors;
						// if we can still submit the form
						if (submit_form) {
							// do it with ajax?
							var go = true;
							if (options.before_submit) { 
								if (options.use_stayput) $.stayPut(false);
								go = options.before_submit();
							}
							
							if (go) { // still go?
								if (options.post_with_ajax)
									form.post(options);
								else {
									// make sure to turn off validation 
									if (options.use_stayput)
										$.stayPut('Saving...');
									form[0].validation_disabled = true;
									setTimeout( function() { 
										form.submit(); 
									}, (alert_box.length) ? 1000 : 0 ); // timeout for alert_box text change
								}
							}
							else
								form.enableButtons();
						}
						else
							form.enableButtons();
					}
				},
				// if there was an error, notify the user
				error: function(XMLHttpRequest, textStatus, errorThrown) {
					$.stayPut(false);
					form.enableButtons();
					
					var message = 'There was an error.';
					// first, try with user-submitted error function
					if (jQuery.isFunction(options.error))
						options.error(XMLHttpRequest, textStatus, errorThrown, form);
					// then try with impromptu
					else if (options.use_impromptu)
						$.prompt($.strip(XMLHttpRequest.responseText));
					// then try with stay put message handling
					else if (options.use_stayput)
						$.stayPut({ message: message, fadeAfter: 10 });
					// finally, just use the alert box
					else
						alert(message);
				}
			});
			
			return false; // the form is not allowed to post automatically
		}
		else
			return true; // form validation has been disabled (because it ran successfully)
	});
	
	
}

/**
 * source: http://www.hunlock.com/blogs/Ten_Javascript_Tools_Everyone_Should_Have
 * @return true when its an array; otherwise, false.
 */
jQuery.isArray = function(testObject) {
	return testObject && !(testObject.propertyIsEnumerable('length')) && typeof testObject === 'object' && typeof testObject.length === 'number';
}

/**
 * Override jQuery.fn.val with a method that knows how to get a value from a select field.
 */
jQuery.fn._val = jQuery.fn.val;
jQuery.fn.val = function(values) {
	if (!this.length || !this[0].type) // not a form field
		return this._val(values);
	else if (this[0].type == 'select-one' || this[0].type == 'select-multiple') {
		if (values != null) {	
			if (!jQuery.isArray(values))
				values = new Array(values);
			this.each(function() {
				var select = this;
				jQuery.each(select.options, function(i, option) {
					option.selected = !(jQuery.inArray(option.value, values) == -1);
				});
			});
		}
		else {
			if (this[0].type == 'select-one') 
				return (this[0].selectedIndex >= 0) ? this[0].options[this[0].selectedIndex].value : null;
			else {
				values = new Array();
				jQuery.each(this[0].options, function(i, option) {
					if (option.selected)
						values.push(option.value);
				});
				return values;
			}
		}
	}
	else if (this[0].type == 'radio') {
		var element_ref = this[0];
		var element = element_ref.form.elements[element_ref.name];
		if (values != null) {
			for (var i=0; i<element.length; i++) {
				if (element[i].value == values)
					element[i].checked = true;
			}
		}
		else {
			for (var i=0; i<element.length; i++) {
				if (element[i].checked)
					return element[i].value;
			}	
		} 
	}
	else
		return this._val(values);
}

jQuery.fn.checked = function(val) {
	if (val != null)
		this.attr('checked', val);
	else
		return this.attr('checked');
}

/**
 * An alternative to the onChange event, this method establishes an observer
 * on the value of the selected elements.  On the frequency specified, checks
 * the value of the field.  If the field value is different from when last
 * checked, callback is fired.
 * 
 * @param frequency A float value in seconds
 * @param callback A function to call when the value changes; the parameters
 * of this function should be three: the field being monitored, the old value,
 * and the new value.
 */ 
jQuery.fn.observeVal = function(frequency, callback) {
	var source = this;
	source.each(function() {
		var field = this;
		var cur_value = null;
		if (field.type == 'text' || field.type == 'password' || field.type == 'hidden') {
			cur_value = field.value;
		}
		else if (field.type == 'select-one' && field.selectedIndex >= 0) {
			cur_value = field.options[field.selectedIndex].value;
		}
		else if (field.type == 'checkbox' || field.type == 'radio') {
			cur_value = (field.checked) ? field.value : null;
		}
		
		
		if (field.__observing && field.__last_value != cur_value) {
			callback(field, field.__last_value, cur_value);
		}
		else
			field.__observing = true;
		field.__last_value = cur_value;
	});
	setTimeout( function() { source.observeVal(frequency, callback); }, frequency * 1000 );  
};

/**
 * Causes the selected objects to flash once in an attempt to gain
 * (or regain) the attention of the user.
 */
jQuery.fn.flash = function() {
	this.animate({
		opacity: 0.25
	}, 250).animate({
		opacity: 1.0
	});
};

/**
 * Capitalizes the first character in this String.
 * @return The new, capitalized String.
 */
String.prototype.capitalize = function() {
	return this.substring(0, 1).toUpperCase() + this.substring(1, this.length);
}

/**
 * Reverses the order of the characters in this String. 
 * @return The new, reversed String.
 */
String.prototype.reverse = function() {
	var newstring = "";
	for (var s=0; s < this.length; s++) {
		newstring = this.charAt(s) + newstring;
	}
	return newstring;
};

/**
 * Retrieves the document object child of an iframe identified by the given id.
 * @param id The id of an iframe in the page
 */
jQuery.getIFrameDocument = function(id) {
	i = document.getElementById(id);
	var d = null;
	if (i.contentDocument)
    	d = i.contentDocument;
    else if (i.contentWindow)
        d = i.contentWindow.document;
  	else
        d = window.frames[id].document;
        
   	return d;
}

/**
 * This method creates an IFRAME specifically for the given button's form,
 * wires that form's submit target to the IFRAME, and hooks a callback
 * function to the onLoad event of the IFRAME.  It is assumed that the result
 * returned by the form's submission will be JSON.  That JSON is evaluated,
 * and the resulting object/array is passed as the single parameter to callback.
 *
 * @param A form button, from which to derive a reference to the form
 * @param fade_in_after The ID of an element that should be faded in upon submission
 *   of the form; this parameter may also be omited, as the function will intelligently 
 *   recognize the absence of the callback parameter. 
 * @param callback A single-parameter function that will be executed when
 *	 the form finishes submitting
 */
jQuery.saveToIFrame = function(button, fade_in_after, callback) {
	var form = button.form;
	var id = form.id + '-iframe';

	if (jQuery.isFunction(fade_in_after)) {
		callback = fade_in_after;
		fade_in_after = null;
	}

	if (!this('#'+id).length) {
		this('body').append('<iframe id="'+id+'" name="'+id+'" style="width:0px; height:0px; border: 0px;" src="about:blank"></iframe>');
		form.target = id;
		this('#'+id).load(function() {
			var d = jQuery.getIFrameDocument(id);
	        if (d.location.href != "about:blank") {
				var result = eval('('+d.body.innerHTML+')');
				if (callback(result)) {
					form.resetFormElements();
					jQuery('#'+form.id).parent().fadeOut((fade_in_after) ? function() {
						jQuery('#'+fade_in_after).fadeIn();
					} : null);
				}
			}
		});
	}
	
	return true;
}

/**
 * Assess and report screen dimensions.
 * Source: http://prototypejs.org
 */
jQuery.viewport = {
	getDimensions: function() {
		var dimensions = { };
	    var B = jQuery.browser;
	    B.WebKit = navigator.userAgent.indexOf('AppleWebKit/') > -1;
	    $.each(['width', 'height'], function(i, d) {
	      var D = d.capitalize();
	      dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
	        (B.opera) ? document.body['client' + D] : document.documentElement['client' + D];
	    });
	    return dimensions;
	},

	getScrollOffsets: function() {
		return {
			left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
			top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
	    };
	}
};

/**
 * @return A guaranteed-unique random element ID = 'random' + a number between 0 and 100000000000000
 */
jQuery.randomId = function() {
	var rand_id = null;
	do { rand_id = 'random'+Math.round(Math.random() * 100000000000000) } while(jQuery('#'+rand_id).length);
	return rand_id;
}

/**
 * Resets all selected elements, or the element children of a selected form element.
 */
jQuery.fn.resetFormElements = function() {
	// this is when fn is a form element:
	if (this[0].tagName.toLowerCase() == 'form' && this[0].elements) {
		for (var i=0; i<this[0].elements.length; i++)
			jQuery.reset(this[0].elements[i]);
	}
	// everything else:
	else {
		this.each(function() {
			// if this is an input tag, reset it
			if (jQuery.inArray(this.tagName.toLowerCase(), ['input','select','textarea']) >= 0) 
				jQuery.reset(this); 
		});
		
		if (this.children().length)
			this.children().resetFormElements();
		
	}
	return this;
};

/**
 * Resets the given form element.
 * @param A Form element tag: not the result of a jQuery selector.
 */ 
jQuery.reset = function(element) {
	if (element.type == 'text' || element.type == 'password' || element.type == 'textarea')
		element.value = '';
	else if (element.type == 'radio') {
		for (var i=0; i<element.length; i++)
			element[i].checked = false;
	}
	else if (element.type == 'checkbox')
		element.checked = false;
	else if (element.type == 'select-one' || element.type == 'select_multiple') 
		element.selectedIndex = null;
};

/*
 * jQuery Impromptu
 * By: Trent Richardson [http://trentrichardson.com]
 * Version 1.5
 * Last Modified: 3/31/2008
 * 
 * Copyright 2008 Trent Richardson
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
jQuery.extend({	
	ImpromptuDefaults: { prefix:'jqi', buttons:{ OK:true }, loaded:function(){}, submit:function(){return true;}, callback:function(){}, opacity:0.6, zIndex: 999, overlayspeed:'slow', promptspeed:'fast', show:'show', focus:0, useiframe:false },
	SetImpromptuDefaults: function(o){ 
		jQuery.ImpromptuDefaults = jQuery.extend({},jQuery.ImpromptuDefaults,o);
	},
	prompt: function(m,o){
		o = jQuery.extend({},jQuery.ImpromptuDefaults,o);
		
		var ie6 = (jQuery.browser.msie && jQuery.browser.version < 7);	
		var b = jQuery(document.body);
		var w = jQuery(window);
		
		var msgbox = '<div style="z-index:10000;" class="'+ o.prefix +'box" id="'+ o.prefix +'box">';		
		if(o.useiframe && ((jQuery.browser.msie && jQuery('object, applet').length > 0) || ie6))//if you want to use the iframe uncomment these 3 lines
			msgbox += '<iframe src="javascript:;" class="'+ o.prefix +'fade" id="'+ o.prefix +'fade"></iframe>';
		else{ 
			if(ie6) $('select').css('visibility','hidden');
			msgbox +='<div class="'+ o.prefix +'fade" id="'+ o.prefix +'fade"></div>';
		}	
		msgbox += '<div class="'+ o.prefix +'" id="'+ o.prefix +'"><div class="'+ o.prefix +'container"><div class="'+ o.prefix +'close">X</div><div class="'+ o.prefix +'message">'+ m +'</div><div class="'+ o.prefix +'buttons" id="'+ o.prefix +'buttons">';
		jQuery.each(o.buttons,function(k,v){ msgbox += '<button name="'+ o.prefix +'button'+ k +'" id="'+ o.prefix +'button'+ k +'" value="'+ v +'">'+ k +'</button>'}) ;
		msgbox += '</div></div></div></div>';
		
		var jqib =b.append(msgbox).children('#'+ o.prefix +'box');
		var jqi = jqib.children('#'+ o.prefix);
		var jqif = jqib.children('#'+ o.prefix +'fade');

		var getWindowScrollOffset = function(){ 
			return (document.documentElement.scrollTop || document.body.scrollTop) + 'px'; 
		};		
		
		var getWindowSize = function(){ 
			var size = {
				width: window.innerWidth || (window.document.documentElement.clientWidth || window.document.body.clientWidth),
				height: window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight)
			};
			return size;
		};
		
		var ie6scroll = function(){ 
			jqib.css({ top: getWindowScrollOffset() }); 
		};
		
		var flashPrompt = function(){
			var i = 0;
			jqib.addClass(o.prefix +'warning');
			var intervalid = setInterval(function(){ 
				jqib.toggleClass(o.prefix +'warning');
				if(i++ > 1){
					clearInterval(intervalid);
					jqib.removeClass(o.prefix +'warning');
				}
			}, 100);			
		};		
		

		var escapeKeyClosePrompt = function(e){
			var kC = (window.event) ? event.keyCode : e.keyCode; // MSIE or Firefox?
			var Esc = (window.event) ? 27 : e.DOM_VK_ESCAPE; // MSIE : Firefox
			if(kC==Esc) removePrompt();
		};

		var positionPrompt = function(){
			var wsize = getWindowSize();
			jqib.css({ position: (ie6)? "absolute" : "fixed", height: wsize.height, width: "100%", top: (ie6)? getWindowScrollOffset():0, left: 0, right: 0, bottom: 0 });
			jqif.css({ position: "absolute", height: wsize.height, width: "100%", top: 0, left: 0, right: 0, bottom: 0 });
			jqi.css({ position: "absolute", top: "100px", left: "50%", marginLeft: ((((jqi.css("paddingLeft").split("px")[0]*1) + jqi.width())/2)*-1) });					
		};
		
		var stylePrompt = function(){
			jqif.css({ zIndex: o.zIndex, display: "none", opacity: o.opacity });
			jqi.css({ zIndex: o.zIndex+1, display: "none" });
		}
		
		var removePrompt = function(callCallback, clicked, msg){
			jqi.remove(); 
			if(ie6)b.unbind('scroll',ie6scroll);//ie6, remove the scroll event
			w.unbind('resize',positionPrompt);			
			jqif.fadeOut(o.overlayspeed,function(){
				jqif.unbind('click',flashPrompt);
				jqif.remove();
				if(callCallback) o.callback(clicked,msg);
				jqib.unbind('keypress',escapeKeyClosePrompt);
				jqib.remove();
				if(ie6 && !o.useiframe) $('select').css('visibility','visible');
			});
		}
		
		positionPrompt();
		stylePrompt();	

		//Events
		jQuery('#'+ o.prefix +'buttons').children('button').click(function(){ 
			var msg = jqi.children('.'+ o.prefix +'container').children('.'+ o.prefix +'message');
			var clicked = o.buttons[jQuery(this).text()];	
			if(o.submit(clicked,msg))				
				removePrompt(true,clicked,msg);
		});
		if(ie6) w.scroll(ie6scroll);//ie6, add a scroll event to fix position:fixed
		jqif.click(flashPrompt);
		w.resize(positionPrompt);
		jqib.keypress(escapeKeyClosePrompt);
		jqi.find('.'+ o.prefix +'close').click(removePrompt);
		
		//Show it
		jqif.fadeIn(o.overlayspeed);
		jqi[o.show](o.promptspeed,o.loaded);
		jqi.find('#'+ o.prefix +'buttons button:eq('+ o.focus +')').focus();//focus the default button
		return jqib;
	}	
});


jQuery.confirmRedirect = function(message, url) {
	$.prompt(
		message, { 
			buttons: { Yes: true, No: false }, 
			callback: function(button) { 
				if (button) document.location = url; 
			}
		}
	);
	return false;
};

jQuery.fn.post = function(options) {

	var form = this;

	options = jQuery.extend({
		use_stayput: true,
		use_impromptu: false,
		success_message: 'Saved.',
		error_message: 'Failed to save. Please try again.',
		reset: false,
		inline_login_url: '/login/login?inline=1'
	}, options);

	if (options.use_stayput)
		$.stayPut('Saving...');

	$.ajax({
		type: 'POST',
		url: this.attr('action'), 
		data: this.serializeArray(), 
		success: function(status) {
			var status = eval('('+status+')');
			
			if (!status.isAuthorized) {
				$.stayPut(false);
				form.enableButtons();
				$.openThickBoxModal(options.inline_login_url);
				return false;
			}
			else if (status.hasErrors) {
				$.stayPut(false);
				$.prompt(status.errors[0]);
			}
			else {
				if (options.success) {
					$.stayPut(false);
					options.success();
				}
				else if (options.use_stayput) 
					$.stayPut({ message: options.success_message, fadeAfter: 3 })
				else if (options.use_impromptu)
					$.prompt(options.success_message)
				else
					alert(options.success_message);
				if (options.reset)
					form.resetFormElements();
			}
			form.enableButtons();
		},
		error: function() {
			if (options.use_stayput) 
				$.stayPut({ message: options.error_message, fadeAfter: 3 })
			else if (options.use_impromptu)
				$.prompt(options.error_message)
			else
				alert(options.error_message);
				
			form.enableButtons();
		}	
	});
}

jQuery.fn.toggleDisabled = function() {
	disabled = !this.attr('disabled');
	if (disabled) this.val('');
	this.attr('disabled', disabled);
};

jQuery.fn.disabled = function(disabled) {
	if (disabled == null)
		disabled = !this.attr('disabled');
	if (disabled) this.val('');
	this.attr('disabled', disabled);
};

/**
 * @param e A DOM event
 * @return Source element of event e
 */ 
jQuery.target = function(e) {
	// source from: http://www.quirksmode.org/js/events_properties.html#target
	var targ;
	if (!e) var e = window.event;
	if (e.target) targ = e.target;
	else if (e.srcElement) targ = e.srcElement;
	if (targ.nodeType == 3) // defeat Safari bug
		targ = targ.parentNode;
	return targ;
}

// TODO: fix this later... 
if (window.Element) {
	Element.prototype.next = function() {
		var possibilities = ['input','select','textarea','a'];
		
		// find me
		if (!this.id) this.id = jQuery.randomId();
		var me = jQuery('#'+this.id);
		var next = null;
		
		do {
			next = (next) ? next.next(':visible') : me.next(':visible');
			if (!next.length) next = next.parent(':visible');
			
		} while (next.length && !jQuery.inArray(next[0].tagName.toLowerCase(), possibilities));
			
		if (next.length && !jQuery.inArray(next[0].tagName.toLowerCase(), possibilities))
			alert(next[0].tagName);
	};
}