﻿var A1 = A1 || {};

(function ($) {
	A1.ready = function(fn) {
		/// <summary>
		///     Specify a function to execute when the DOM is fully loaded.
		/// </summary>
		/// <param name="fn" type="Function">
		///     A function to execute after the DOM is ready.
		/// </param>
		
		if (typeof (Sys) !== "undefined") {
			Sys.Application.add_load(fn);
			Sys.Application.notifyScriptLoaded();
			
			if ($.isReady) {
				fn();
			}
		} else {
			$(document).ready(fn);
		}
		
	};

	// general purpose javascript methods, not specifically meant to be extensions to jQuery
	A1.Utils = A1.Utils || { };

	var urlParams;
	A1.Utils.urlParameters = function() {
		/// <summary>
		///     Returns a javascript object that contains the Querystring parameters
		/// </summary>

		if (urlParams) return urlParams;
		if (!urlParams) urlParams = { };
		var e,
			a = /\+/g,  // Regex for replacing addition symbol with a space
			r = /([^&=]+)=?([^&]*)/g,
			d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
			q = window.location.search.substring(1);

			while (e = r.exec(q))
				urlParams[d(e[1])] = d(e[2]);

		return urlParams;
	};

	A1.Utils.isDomElement = function(element) {
		/// <summary>
		///     returns true if the given element is a DOM element
		/// </summary>

		if (!element)
			return false;
		
		var obj = element;
		
		if (obj.jquery)
			obj = obj[0]; // unwrap as a jQuery element

		try {
			//Using W3 DOM2 (works for FF, Opera and Chrom)
			return obj instanceof HTMLElement;
		} catch(e) {
			//Browsers not supporting W3 DOM2 don't have HTMLElement and
			//an exception is thrown and we end up here. Testing some
			//properties that all elements have. (works on IE7)
			return (typeof obj === "object") &&
				(obj.nodeType === 1) && (typeof obj.style === "object") &&
					(typeof obj.ownerDocument === "object");
		}

	};

	$.fn.outerHtml = $.fn.outerHtml || function () {
		///	<summary>
		///	Returns a string representation of the selected element and its children.
		///	</summary>
		///	<returns type="string" />			
		return $("<div>").append($(this).clone()).remove().html();
	};
	
	$.fn.setDefaultFormButton = function () {
		///	<summary>
		///	Sets the selected element to be the default form button, which will be clicked when a user clicks return in form input controls
		///	</summary>
		///	<returns type="jQuery" />				
		var button = $(this);
		$("form input").keypress(function (e) {
			if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) {
				button.click();
				return false;
			} else {
				return true;
			}
		});

		return button;
	};

	$.fn.applySpriteIcon = function(options) {
		var opts = typeof(options) == "string" ? {
			spriteIconClass: options,
			showText: true
		} : options;

		return this.each(function() {
			if (!$(this).children().hasClass(opts.spriteIconClass)) {
				var linkCss = 'spriteIconLink';
				var disabled = '';
				var isDisabled = $(this).is(':disabled') || $(this).data('disabled');
				if (isDisabled) {
					disabled = ' gray';
					linkCss = 'spriteIconLinkDisabled ui-state-disabled';
				}

				var icon = '<span class="ui-silk spriteBeforeText inline {0}{1}"></span>'.format(opts.spriteIconClass, disabled);

				// IE7 (and below?) hides the entire link when the span is added - adding the space seems to overcome the rendering bug.
				if($.browser.msie && $.browser.version < 8) {
					icon = ' ' + icon;
				}
				var titleText = $(this).prop('title');
				if (titleText.length == 0) {
					titleText = $(this).text();
				}
				if (!opts.showText) {
					$(this).text("");
				}
				$(this).addClass(linkCss).prop('title', titleText).prepend(icon);

			}
		});
	};

	$.fn.applyEditSpriteIcon = function () {
		return this.each(function () {
			$(this).applySpriteIcon("ui-silk-pencil");
		});
	};

	$.fn.applyAddSpriteIcon = function () {
		return this.each(function () {
			$(this).applySpriteIcon("ui-silk-pencil-add");
		});
	};

	$.fn.applyDeleteSpriteIcon = function (options) {
		///	<summary>
		///	Appends a delete sprite icon to the selected element.  Elements with attribute of "data-confirm" will show a confirmation message
		///	</summary>
		/// <param name="options">A structure containing the following options: 
		///	onClick : callback when the delete link is clicked
		/// </param>
		///	<returns type="jQuery" />		
		var opts = $.extend({}, $.fn.applyDeleteSpriteIcon.defaults, options);
		return this.each(function () {
			var confirmMsg = $(this).data("confirm") || '';

			$(this).applySpriteIcon("ui-silk-delete")
				.click(function () {
					var proceed = !($(this).is(":disabled") || false);
					if (!proceed) return false;

					if (confirmMsg != '') {
						proceed = confirm(confirmMsg);
					}
					if (proceed) {
						proceed = opts.onClick();
					}
					return proceed;
				});
		});
	};

	$.fn.applyDeleteSpriteIcon.defaults = {
		onClick: function () { }
	};

	$.fn.applyGroupHeader = function (options) {
		var opts = options || {
			headerTitle: null,
			autoOpen: true
		};
		return this.each(function () {
			if (!$(this).hasClass('headerContent')) {
				var title = opts.headerTitle || $(this).data('header');
				var header = '<div class="inputLineSmall"><div class="headerGroup"><span class="resultSmallHeader inputLine SubHeaderText indent">{0}</span></div></div>'.format(title);
				$(this).addClass('headerContent').before(header);
				initializeGroupHeaders({
					autoOpen: opts.autoOpen,
					headerGroupSet: $(this).prev().find(".headerGroup")
				});
			}
		});
	};

	function initializeGroupHeaders(options) {
		var headerGroupSet = options.headerGroupSet;
		var autoOpen = options.autoOpen;
		var openCss = "flex_header_open";
		var closeCss = "flex_header_close";
		var openBannerCss = "openDropShadowBanner";
		var closeBannerCss = "closeDropShadowBanner";

		headerGroupSet.click(function () {
			var headerGroup = $(this);
			headerGroup.parent().next(".headerContent").slideToggle('medium', function () {
				headerGroup.toggleClass(openCss);
				headerGroup.toggleClass(closeCss);
				headerGroup.parent().toggleClass(openBannerCss);
				headerGroup.parent().toggleClass(closeBannerCss);
			});
		});

		headerGroupSet.parent().addClass(autoOpen ? openBannerCss : closeBannerCss);
		headerGroupSet.addClass(autoOpen ? openCss : closeCss);

		if (!autoOpen) {
			headerGroupSet.each(function () {
				var headerGroup = $(this);
				headerGroup.parent().next(".headerContent").hide();
			});
		}

	};

	$.fn.displayError = function (errorMsg) {
		return this.each(function () {
			var container = $(this);
			var errorMarkup = '<span class="standout" title="{0}">Error</span>'.format(errorMsg);

			container.children().remove();
			$(errorMarkup).appendTo(container).applySpriteIcon("ui-silk-error").fadeIn().delay(2000);
		});
	};
	
	$.fn.clearErrorSummary = function() {
		return this.each(function() {
			$(this).find(".ErrorSummary").html('');
		});
	};

	$.fn.displayErrorSummary = function(errMsg) {
		return this.each(function() {
			var $errSummary = $(this).find(".ErrorSummary");
			if (errMsg == '') {
				$errSummary.hide();
			} else {
				$errSummary.html('').displayMessage(errMsg, "ui-silk-error");
			}
		});
	};

	$.fn.displayMessage = function (message, silkIcon, cssStyles, callback) {
		var targetContext = $(this);
		var msg = $("<span></span>").html(message).addClass(cssStyles).applySpriteIcon(silkIcon);
		targetContext.append(msg).slideDown('medium', function () {
			if (callback != null) {
				callback(msg);
			}
		});
		return targetContext;
	};

	$.fn.displaySuccess = function (msg, callback) {
		var container = $(this);
		container.children().hide();
		container.displayMessage(msg, "ui-silk-tick", "standout", 
			function (msgContext) {
				setTimeout(function () {
					msgContext.remove();
					callback();
					container.children().delay(250).fadeIn('slow');
				}, 1500);
			});
	};

	$.fn.linkifyText = function () {
		return this.each(function () {
			var newText = $(this).text().replace(/[A-Za-z]+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&\?\/.=]+/, function (m) { return m.link(m); });
			$(this).html(newText);
		});
	};

	$.fn.applyMaxHeight = function (pixelUnit) {
		return this.each(function () {
			if ($(this).height() > pixelUnit) {
				$(this).height(pixelUnit + 'px');
			}
		});
	};

	$.fn.execIconLinkAction = function (options) {
		///	<summary>
		///	Calls an ajax service specified by the given element, and formats the response.  By convention, this is primarily used with anchors (href), but can be overridden by specifying the target property in the options parameter
		///	</summary>
		/// <param name="options">A structure containing the following options: 
		///     onSuccess : callback when the ajax request succeeds. defaults to the link's container fading out.
		///		errorMessage : error message displayed when the ajax request fails.
		///		target : the url of the ajax service.
		/// </param>
		///	<returns type="jQuery" />		
		var opts = $.extend({}, $.fn.execIconLinkAction.defaults, options);
		return this.each(function () {
			var container = $(this);

			$.ajax({
				url: opts.target(container),
				type: "POST",
				dataType: "json",
				data: opts.postedData(container),
				success: function (reply) {
					if (reply.Success) {
						opts.onSuccess(container, reply.Entity);
					}
					else {
						opts.onSuccessViolations(container, reply);
					}
				},
				error: function (httpRequest, textStatus, errorThrown) {
					opts.onServerError(container, httpRequest, textStatus, errorThrown);
				}
			});
		});
	};

	A1.Standards = A1.Standards || { };
	$.extend(A1.Standards, {
		errorMessage : "An error occurred while processing your request.",
		clickActionFailedDueToViolations : function (element, reply) {
			var title = "{0}  Details: ".format(this.errorMessage);
			var sep = "";
			var details = "";
			$.each(reply.Violations, function (index, violation) {
				details += sep + violation.ErrorMessage;
				sep = ", ";
			});
			title = title + details;

			element.parent().displayError(title);
		},
		clickActionFailedDueToServerError : function (element, httpRequest, textStatus, errorThrown) {
			var details = "Could not process your request.";
			if (errorThrown != undefined && errorThrown != "") {
				details = errorThrown;
			}
			var errMsg = "{0}  Details: {1}".format(this.errorMessage, details);
			element.parent().displayError(errMsg);
		}
	});

	$.fn.execIconLinkAction.defaults = {
		onSuccess: function (container, reply) { container.fadeOut(); },
		errorMessage: "An error occurred while processing your request.",
		target: function (container) { return container.attr('href'); },
		postedData: function (container) { return {}; }
	};

	// see http://stackoverflow.com/questions/483741/how-to-determine-which-html-page-element-has-focus
	$.fn.hasFocus = function () {
		if (document.activeElement)
			return ((this.length == 1) && (this[0] == document.activeElement));
	};

	$.fn.renderedHeight = function (ambientCss) {
		/// <summary>
		///		Calculates the height of an element regardless of its visibility
		/// </summary>
		/// <param name="ambientCss">Allows you to recreate the surrounding CSS that affects the height (e.g. font-size)</param>
		
		var defaultCss = { position: "absolute", visibility: "hidden", display: "block", top: -10000, left: -10000 };
		var cloneCss =  $.extend(defaultCss, ambientCss);

		var snapshot = this.clone().css(cloneCss);
		snapshot.appendTo("body");
		var height = snapshot.height();
		snapshot.remove();
		return height;
	};

	$.fn.equalizeHeights = function (ambientCss) {
		/// <summary>
		///		Equalizes the heights of a set of elements by setting their height to the tallest height in the set.
		/// </summary>

		var cloneMaxHeightCalculation = function (element) {
			return $(element).renderedHeight(ambientCss);
		};

		var simpleMaxHeightCalculation = function (element) {
			return $(element).height();
		};

		var elements = this;

		var calculateHeight = elements.is(":visible") ? simpleMaxHeightCalculation : cloneMaxHeightCalculation;
		var heights = [];
		elements.each(function () {
			heights.push(calculateHeight(this));
		});

		var maxHeight = heights.max();
		elements.each(function () {
			$(this).height(maxHeight);
		});

		return elements;
	};

	function formatThisString() {
		var txt = this;
		for (var i = 0; i < arguments.length; i++) {
			var exp = new RegExp('\\{' + (i) + '\\}', 'gm');
			txt = txt.replace(exp, arguments[i]);
		}
		return txt;
	};

	if (!String.prototype.format) {
		String.prototype.format = formatThisString;
	}

	Sys.Application.add_load(function () {
		$(function () {
			$('a[rel="external bookmark"],a[rel="alternate"],a.popupWindow').die().live("click", function () {
				var $link = $(this);
				var winHeight = $link.data("height") || 640;
				var winWidth = $link.data("width") || 550;

				var options = "location=no,menubar=no,toolbar=no,status=no,height={0},width={1},scrollbars=yes,resizable=yes"
								.format(winHeight, winWidth);
				var newwin = window.open($(this).attr('href'), "", options);
				newwin.focus();
				return false;
			});

			$('.autoicon.delete').each(function () {
				$(this).applyDeleteSpriteIcon();
			});

			$('.autoicon.edit').each(function () {
				$(this).applyEditSpriteIcon();
			});
		});
	});

})(jQuery);

function _dom_trackActiveElement(evt) {
	if (evt && evt.target) {
		document.activeElement = evt.target == document ? null : evt.target;
	}
}

function _dom_trackActiveElementLost(evt) {
	document.activeElement = null;
}

if (!document.activeElement && document.addEventListener) {
	document.addEventListener("focus", _dom_trackActiveElement, true);
	document.addEventListener("blur", _dom_trackActiveElementLost, true);
}

/* Array prototype methods */
Array.prototype.max = Array.prototype.max || function () {
	return Math.max.apply(Math, this);
};

/* Date prototype methods */
Date.prototype.addDays = function(days) {
	this.setDate(this.getDate() + days);
	return this;
};

Date.prototype.addHours = function(hours) {
	this.setHours(this.getHours() + hours);
	return this;
};

Date.prototype.addMilliseconds = function(milliseconds) {
	this.setMilliseconds(this.getMilliseconds() + milliseconds);
	return this;
};

Date.prototype.addMinutes = function(minutes) {
	this.setMinutes(this.getMinutes() + minutes, this.getSeconds(), this.getMilliseconds());
	return this;
};

Date.prototype.addMonths = function(months) {
	this.setMonth(this.getMonth() + months, this.getDate());
	return this;
};

Date.prototype.addSeconds = function(seconds) {
	this.setSeconds(this.getSeconds() + seconds, this.getMilliseconds());
	return this;
};

Date.prototype.addYears = function(years) {
	this.setFullYear(this.getFullYear() + years);
	return this;
};

/*
* Date Format 1.2.3
* (c) 2007-2009 Steven Levithan <stevenlevithan.com>
* MIT license
*
* Includes enhancements by Scott Trenda <scott.trenda.net>
* and Kris Kowal <cixar.com/~kris.kowal/>
*
* Accepts a date, a mask, or a date and a mask.
* Returns a formatted version of the given date.
* The date defaults to the current date/time.
* The mask defaults to dateFormat.masks.default.
*/

var dateFormat = function () {
	var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
		timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
		timezoneClip = /[^-+\dA-Z]/g,
		pad = function (val, len) {
			val = String(val);
			len = len || 2;
			while (val.length < len) val = "0" + val;
			return val;
		};

	// Regexes and supporting functions are cached through closure
	return function (date, mask, utc) {
		var dF = dateFormat;

		// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
		if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
			mask = date;
			date = undefined;
		}

		// Passing date through Date applies Date.parse, if necessary
		date = date ? new Date(date) : new Date;
		if (isNaN(date)) throw SyntaxError("invalid date");

		mask = String(dF.masks[mask] || mask || dF.masks["default"]);

		// Allow setting the utc argument via the mask
		if (mask.slice(0, 4) == "UTC:") {
			mask = mask.slice(4);
			utc = true;
		}

		var _ = utc ? "getUTC" : "get",
			d = date[_ + "Date"](),
			D = date[_ + "Day"](),
			m = date[_ + "Month"](),
			y = date[_ + "FullYear"](),
			H = date[_ + "Hours"](),
			M = date[_ + "Minutes"](),
			s = date[_ + "Seconds"](),
			L = date[_ + "Milliseconds"](),
			o = utc ? 0 : date.getTimezoneOffset(),
			flags = {
				d: d,
				dd: pad(d),
				ddd: dF.i18n.dayNames[D],
				dddd: dF.i18n.dayNames[D + 7],
				m: m + 1,
				mm: pad(m + 1),
				mmm: dF.i18n.monthNames[m],
				mmmm: dF.i18n.monthNames[m + 12],
				yy: String(y).slice(2),
				yyyy: y,
				h: H % 12 || 12,
				hh: pad(H % 12 || 12),
				H: H,
				HH: pad(H),
				M: M,
				MM: pad(M),
				s: s,
				ss: pad(s),
				l: pad(L, 3),
				L: pad(L > 99 ? Math.round(L / 10) : L),
				t: H < 12 ? "a" : "p",
				tt: H < 12 ? "am" : "pm",
				T: H < 12 ? "A" : "P",
				TT: H < 12 ? "AM" : "PM",
				Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
				o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
				S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
			};

		return mask.replace(token, function ($0) {
			return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
		});
	};
} ();

// Some common format strings
dateFormat.masks = {
	"default": "ddd mmm dd yyyy HH:MM:ss",
	shortDate: "m/d/yy",
	mediumDate: "mmm d, yyyy",
	longDate: "mmmm d, yyyy",
	fullDate: "dddd, mmmm d, yyyy",
	shortTime: "h:MM TT",
	mediumTime: "h:MM:ss TT",
	longTime: "h:MM:ss TT Z",
	isoDate: "yyyy-mm-dd",
	isoTime: "HH:MM:ss",
	isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
	isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
};

// Internationalization strings
dateFormat.i18n = {
	dayNames: [
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
		"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
	],
	monthNames: [
		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
		"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
	]
};

// For convenience...
Date.prototype.format = function (mask, utc) {
	return dateFormat(this, mask, utc);
};

String.prototype.fromJsonDate = function () {
	if (this instanceof Date)
		return this;
	
	return new Date(parseInt(this.substr(6)));
};

