(function($) {

	/*---------------------------------------------------------------------
	 * CONSTANTS
	 *--------------------------------------------------------------------*/

	$.utils = {

		fromQueryString : function(query) {

			hash = {}

			if (query.indexOf("?") >= 0) {

				query = query.substring(query.indexOf("?") + 1);
			}

			var variables = query.split("&");

			for ( var i = 0; i < variables.length; i++) {

				var values = variables[i].split("=");

				var name = values[0];
				var value = values[1];

				hash[name] = value;
			}

			return hash;
		},

		getYoutubeUrl : function(url) {

			var template = window.location.protocol
					+ "//www.youtube.com/v/{video}?hl=en_US&fs=1&rel=0&color1=0x3a3a3a&color2=0x999999&border=0";

			var context = $.utils.fromQueryString(url);

			return template.replace("{video}", context.v);
		},

		positiveOrZero : function(value) {

			var result = parseInt(value);

			if (result < 0) {
				result = 0;
			}

			return result;
		}
	};

	$.realdirect = {

		tooltips : function(e) {

			var triggers = $(".trigger");

			triggers.tooltip({

				// custom positioning
				position : 'bottom right',

				// move tooltip a little bit to the right
				offset : [ -20, 10 ],

				opacity : 1,

				// do not initialize tooltips until they are used
				lazy : false,

				// there is no delay when the mouse is moved away from the
				// trigger
				delay : 0

			});

			triggers.dynamic({

				// customized configuration on bottom edge
				left : {

					// slide downwards
					direction : 'right'

				}
			});
		},

		/**
		 * Session controller function which warns the user before the session
		 * expires giving him the option to refresh the session or to leave it
		 * die.
		 * 
		 * @param time
		 *            the number of seconds until the session expires
		 * 
		 * @param refreshUrl
		 *            the url we will submit to refresh the session
		 * 
		 * @param message
		 *            the message displayed for the user within the confirmation
		 *            alert
		 */
		startSession : function(time /* in seconds */, refreshUrl, message) {

			/*
			 * transform time in milliseconds
			 */
			var countdown = time * 1000;

			message = message || "Your RealDirect session is about to expire "
					+ "due to inactivity. Please click 'OK' within the "
					+ "next 5 minutes to extend your session and stay logged "
					+ "in. Canceling or ignoring this message will cause "
					+ "you to be logged out after 5 additional minutes of "
					+ "inactivity, resulting in a loss of any unsaved data.";

			/*
			 * subtract 5 minutes to give enough room for the session to expire
			 */
			countdown -= (5 * 60 * 1000);

			setTimeout(function() {

				var answer = confirm(message);

				if (answer) {

					/*
					 * refresh session on the backend and restart session
					 * countdown
					 */
					$.post(refreshUrl, null, function() {

						$.realdirect.startSession(time, refreshUrl, message);
					});
				}

			}, countdown);
		},

		/**
		 * Function called whenever the application starts. We need to show a
		 * warning to the user if the browser is not supported, which may
		 * prevent the user from seeing the site properly.
		 */
		validateBrowser : function() {

			var version = Number($.browser.version);

			if ($.browser.msie && version <= 6) {

				alert("RealDirect has limited legacy support for Internet Explorer 6.\nTo get the full experience, please upgrade to IE7/8/9.\nYou may also use most versions of Firefox, Chrome, and Safari.");
			}
		}
	};

	/*---------------------------------------------------------------------
	 * END OF CONSTANTS
	 *--------------------------------------------------------------------*/

	/*---------------------------------------------------------------------
	 * FUNCTIONS
	 *--------------------------------------------------------------------*/

	/**
	 * handler invoked whenever any keyboard key is pressed within text inputs
	 * currently registered on the browser's DOM.
	 * 
	 * We make sure to trigger a fake change so the same can be processed before
	 * triggering submit on a parent form
	 * 
	 * @param event
	 *            the keyboard event
	 */
	function fieldKeyDownHandler(event) {

		if (event.keyCode == 13) {
			fieldChangeHandler.apply(this);
		}
	}

	/**
	 * Handler invoked whenever any text input currently registered on the
	 * browser's DOM changes.
	 * 
	 * We make sure to replace any invalid characters to avoid any char set
	 * conversation exceptions at the back-end.
	 */
	function fieldChangeHandler() {

		var field = $(this);

		var value = field.val();

		value = value.replace(new RegExp("‒", "g"), "-");
		value = value.replace(new RegExp("–", "g"), "-");
		value = value.replace(new RegExp("—", "g"), "-");
		value = value.replace(new RegExp("―", "g"), "-");
		value = value.replace(new RegExp(" ", "g"), " ");

		field.val(value);
	}

	function startup() {

		// Open CSC tooltip in better location
		$(".payment .trigger, .upgrade-cc .trigger").tooltip({
			position : "top right"
		});

		$('div.dropin-notifier').delay(750).slideDown("slow");

		/* Show/hide the mag glass on the image gallery */

		$('div.gallery-image-wrapper').hover(function() {
			$(this).find('.enlarge').fadeIn('fast');
		}, function() {
			$(this).find('.enlarge').fadeOut('fast');
		});

		/* Homepage testimonial rotation */

		var testimonialObj = $('.testimonial-quotes > div');
		var testimonialArr = $.makeArray(testimonialObj);
		var randomQuote = Math.floor(Math.random() * testimonialObj.length);
		$('.testimonial').html(testimonialObj[randomQuote]);

		// Home page slideshow

		$(".slide-nav").tabs(".slides > div", {

			// enable "cross-fading" effect
			effect : 'fade',
			fadeOutSpeed : "slow",

			// start from the beginning after the last tab
			rotate : true

		// use the slideshow plugin. It accepts its own configuration
		}).slideshow({
			autoplay : true,
			interval : 10000,
			clickable : false
		});

		/*
		 * pre process all text fields removing any invalid characters
		 */
		$("input[type=text]").live("change", fieldChangeHandler);
		$("input[type=text]").live("keydown", fieldKeyDownHandler);
		$("textarea").live("change", fieldChangeHandler);
		$("textarea").live("change", fieldKeyDownHandler);

		/*
		 * disable right clicking on links with class no-rclick
		 */
		$("a.no-rclick").bind("contextmenu", function() {
			return false;
		});

		/*
		 * initialize date-picker components
		 */
		try {
			$(".date-picker").each(function() {

				var picker = $(this);

				if (!picker.attr("id")) {
					picker.generateId("picker");
				}

				picker.datepicker();

			});
		} catch (e) {
		}

		$.realdirect.tooltips();
		
		$.realdirect.validateBrowser();

	}

	/*---------------------------------------------------------------------
	 * END OF FUNCTIONS
	 *--------------------------------------------------------------------*/

	/*---------------------------------------------------------------------
	 * JQUERY PLUGINS
	 *--------------------------------------------------------------------*/

	/**
	 * Simple plugin that retrieves the parent for a component using a given
	 * selector similar to jQuery.fn.parentsUntil() but instead of returning a
	 * list of parents it returns only the parent that matches the given
	 * selector.
	 * 
	 * @param the
	 *            selector for the parent element we want to find.
	 * 
	 * @return The parent component down to the given selector.
	 * 
	 * @function
	 * @memberOf jQuery.fn
	 * @name jQuery.fn.parentTo
	 */
	$.fn.parentTo = function(selector) {

		var parent = this.parent();

		if (parent.is(selector)) {

			return parent;
		}

		return this.parentsUntil(selector).last().parent();
	};

	/**
	 * Simple plugin that configures a confirmation dialog by using jQueryUI's
	 * dialog with two yes/cancel buttons, a title, a body message and a
	 * callback function.
	 * 
	 * @param title
	 *            the title displayed within the confirmation dialog
	 * 
	 * @param message
	 *            the message displayed within the confirmation dialog
	 *            (dom/jquery/string)
	 * 
	 * @param callback
	 *            function called once the dialog has been closed with a boolean
	 *            as parameter containing the answer (true/false)
	 * 
	 * @param conf
	 *            configuration object containing :
	 *            <ul>
	 *            <li>yes: title presented within the yes button</li>
	 *            <li>cancel: title presented within the cancel button</li>
	 *            </ul>
	 * 
	 */
	$.fn.confirmdialog = function(title, message, callback, conf) {

		var self = this;
		var conf = conf || {};

		var yes = conf.yes || "YES";
		var cancel = conf.cancel || "cancel";

		self.dialog({
			modal : true,
			autoOpen : false,
			resizable : false,
			draggable : false,
			width : 500,
			show : 'fade',
			hide : 'fade'
		});

		var dialog = self.parentsUntil("ui-dialog").parent().first();

		/*
		 * set title and content
		 */
		self.dialog('option', 'title', title);
		self.find(".modal-content").html(message);

		var buttons = {};

		if (typeof conf.yes != 'boolean' || conf.yes) {

			buttons[yes] = function() {

				self.dialog('close');

				if (callback) {
					callback.apply(self, [ true ]);
				}
			};
		}

		if (typeof conf.cancel != 'boolean' || conf.cancel) {

			buttons[cancel] = function() {

				self.dialog('close');

				if (callback) {
					callback.apply(self, [ false ]);
				}
			};
		}

		/*
		 * configure all buttons
		 */
		self.dialog('option', 'buttons', buttons);

		dialog.find("button:contains(" + cancel + ")").addClass("cancel");
		dialog.find("button:contains(" + yes + ")").addClass("confirm");

		self.show();
		self.dialog('open');

		/*
		 * don't break the chain
		 */
		return self;
	};

	$.fn.formdata = function(data) {

		var self = this;

		if (typeof data == "undefined") {

			var getFieldValue = function(name) {

				var fields = instance.find("[name=" + name + "]");

				if (fields.is(":radio")) {

					return fields.filter(":checked").val();
				}

				return fields.last().val();
			};

			var fields = self.serializeArray();
			var object = {};

			for ( var i = 0; i < fields.length; i++) {

				var field = fields[i];

				object[field.name] = field.value;
			}

			return object;
		}

		var setFieldValue = function(name, value) {

			var field = name;

			if (typeof field == "string") {

				field = self.find("[name=" + field + "]");

				/*
				 * multiple fields? might be a comma separated value
				 */
				if (field.length > 1) {

					/*
					 * update radio buttons
					 */
					if (field.is(":radio")) {

						field.each(function() {

							var $field = $(this);

							if ($field.val() == value) {

								$field.attr("checked", "checked");

							} else {

								$field.removeAttr("checked");
							}
						});

						return;
					}

					if (typeof value == 'string') {

						var values = value.split(",");

						for ( var i = 0; i < values.length; i++) {

							var fields = field.filter("[value=" + values[i]
									+ "]");

							fields.each(function() {

								setFieldValue($(this), values[i]);
							});
						}

						// field = fields.filter("[type=hidden]");
					}

				} else {

					setFieldValue(field, value);
				}

				return;
			}

			if (field.attr("type") == "checkbox") {

				if (field.val() == String(value)
						|| (typeof value == 'boolean' && value)) {

					field.attr("checked", "checked");

				} else {

					field.removeAttr("checked");
				}

				// field.attr("checked", String(value) == field.val());
			} else {

				field.val(value);
			}
		};

		for ( var name in data) {

			setFieldValue(name, data[name]);
		}

		return self;
	};

	$.fn.maxlength = function() {

		this.not('.placeholder').each(
				function() {

					var element = $(this);

					if (!element.data("maxlength")) {

						var api = new function() {

							var instance = element;
							var input = instance
									.find("input[type='text'], textarea");
							var counter = instance.find(".counter");
							var length = parseInt($.utils.positiveOrZero(input
									.attr("maxlength"))
									|| input.data("_maxlength")
									|| counter.text());

							input.data("_maxlength", length);

							input.keyup(function() {

								counter.text(length - input.val().length);

								var text = input.val();

								if (parseInt(counter.text()) < 0) {
									text = text.substring(0, length);
									input.val(text);
									counter.text("0");
								}
							});

							input.keyup();
						};

						element.data("maxlength", api);
					}
				});
	};

	/**
	 * Simple plugin that generates an id with the given prefix, applies to the
	 * HTML element and return the generated ID as String
	 * 
	 * @param prefix
	 *            the prefix
	 * 
	 * @return generated ID as String
	 */
	$.fn.generateId = function(prefix) {

		var id = prefix + String(Math.random()).substring(2);

		this.attr("id", id);

		return id;
	}

	$.fn.dropDownLogin = function(options) {

		/*
		 * / Plugin to manage drop down login box on marketing site
		 */

		var settings = {};

		var form = this;
		var formErrors = form.find('#login_error_id');

		var options = $.extend(settings, options);

		$('body').click(function() {

			$('div#dropdown-login').slideUp('fast', function() {

				formErrors.hide();
			});
		});

		$('div#dropdown-login').click(function(event) {
			event.stopPropagation();
		});

		$('.register-login a.login').click(function(event) {
			event.stopPropagation();
			$('div#dropdown-login').slideToggle('fast');
			$('input#username').focus();
			return false;
		});

		function submit() {

			var username = $('.login-forms #username').val();
			var password = $('.login-forms #password').val();
			var next = $.utils.fromQueryString(location.href).next;

			$
					.post(
							options.url,
							{
								'username' : username,
								'password' : password
							},
							function(data) {

								function errorHandler() {

									form.find('input#username').focus();

									formErrors.html(res.message);

									if (formErrors.is(':hidden')) {

										formErrors.slideDown('fast')/* .delay(2800).slideUp(1200) */;
									}

									form.find('input').keyup(function() {

										formErrors.slideUp(1200);
									});
								}

								var res = $.evalJSON(data);
								if (res.success) {

									if (next) {
										window.location.href = next;
									} else {
										window.location.href = res.path;
									}

								} else {

									errorHandler();
								}
							})
		}

		form.submit(function() {

			submit();

			return false
		})
	}

	$.fn.listingCompleteGraph = function(options) {

		/*
		 * Plugin to manage listing graph completion
		 */
		var settings = {
			'delay' : '1350',
			'current' : '0',
			'rate' : '1',
			'completed' : '90'
		};

		var options = $.extend(settings, options);
		var instance = this;
		var completedElement = instance.find("#completed");
		var cssWidth = options.completed + "%";

		var counter = setInterval(function() {

			if (options.current >= options.completed)
				clearInterval(counter);

			$("#amount").text(options.current + "%");

			options.current = parseInt(options.current)
					+ parseInt(options.rate);

		}, options.delay / (options.completed / options.rate));

		// animate the graph on page refresh
		completedElement.animate({

			width : cssWidth

		}, options.delay);

		// count out the precent as the graph animates
		counter;

		// don't break the chain
		return instance;
	};

	// ---------------------------------
	// PATCH TO JQUERY-TOOLS TABS
	// ---------------------------------

	if ($.tools && $.tools.tabs) {

		var superTabs = $.fn.tabs;

		/**
		 * Custom Tabs component based on jQuery-tools tabs plugin. We just
		 * patch its initialization so we can set the proper initial index based
		 * on window.location.hash
		 */
		$.fn.tabs = function(containers, conf) {

			if (conf && conf.history && window.location.hash) {

				var children = typeof containers == "string" ? $(containers)
						: containers;

				var name = window.location.hash.replace("#", "");

				var tabs = this.find("[href]");

				var tab = tabs.filter("[href=" + name + "]");

				var index = tabs.index(tab);

				if (index) {
					conf.initialIndex = index;
				}
			}

			return superTabs.apply(this, [ containers, conf ]);
		};
	}

	/*---------------------------------------------------------------------
	 * END OF JQUERY PLUGINS
	 *--------------------------------------------------------------------*/

	$(function() {
		startup();
	});

})(jQuery);

