﻿/*!
* Fusion JavaScript Framework v3.0.0
* Copyright (c) 2009 Webfuel
* http://www.webfuelstudios.co.uk
*
*/

(function($) {

	var _modalElement = null; 	// The jquery modal element
	var _pre = 'uid', _uid = 10000; // UID stamps

	var _popupElement = null; 	// The jquery popup element
	var _popupOptions = null; 	// Options used by the current popup request

	Json = function() {

		function f(n) {
			// Format integers to have at least two digits.
			return n < 10 ? '0' + n : n;
		}

		Date.prototype.toJSON = function(key) {

			return this.getUTCFullYear() + '-' +
				 f(this.getUTCMonth() + 1) + '-' +
				 f(this.getUTCDate()) + 'T' +
				 f(this.getUTCHours()) + ':' +
				 f(this.getUTCMinutes()) + ':' +
				 f(this.getUTCSeconds()) + 'Z';
		};

		Number.prototype.toJSON =
		Boolean.prototype.toJSON = function(key) {
			return this.valueOf();
		};


		String.prototype.toJSON = function(key) {
			return this.valueOf();
		};

		var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
		escapeable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
		gap,
		indent,
		meta = {    // table of character substitutions
			'\b': '\\b',
			'\t': '\\t',
			'\n': '\\n',
			'\f': '\\f',
			'\r': '\\r',
			'"': '\\"',
			'\\': '\\\\'
		},
		rep;

		function quote(string) {

			escapeable.lastIndex = 0;
			return escapeable.test(string) ?
		'"' + string.replace(escapeable, function(a) {
			var c = meta[a];
			if (typeof c === 'string') {
				return c;
			}
			return '\\u' + ('0000' +
					(+(a.charCodeAt(0))).toString(16)).slice(-4);
		}) + '"' :
		'"' + string + '"';
		}

		function str(key, holder) {
			var i,          // The loop counter.
			k,          // The member key.
			v,          // The member value.
			length,
			mind = gap,
			partial,
			value = holder[key];

			if (value && typeof value === 'object' &&
			typeof value.toJSON === 'function') {
				value = value.toJSON(key);
			}

			if (typeof rep === 'function') {
				value = rep.call(holder, key, value);
			}

			switch (typeof value) {
				case 'string':
					return quote(value);

				case 'number':
					return isFinite(value) ? String(value) : 'null';

				case 'boolean':
				case 'null':
					return String(value);

				case 'object':
					if (!value) {
						return 'null';
					}

					gap += indent;
					partial = [];

					if (typeof value.length === 'number' &&
						!(value.propertyIsEnumerable('length'))) {

						length = value.length;
						for (i = 0; i < length; i += 1) {
							partial[i] = str(i, value) || 'null';
						}

						v = partial.length === 0 ? '[]' :
							gap ? '[\n' + gap +
							partial.join(',\n' + gap) + '\n' +
							mind + ']' :
							'[' + partial.join(',') + ']';
						gap = mind;
						return v;
					}

					if (rep && typeof rep === 'object') {
						length = rep.length;
						for (i = 0; i < length; i += 1) {
							k = rep[i];
							if (typeof k === 'string') {
								v = str(k, value);
								if (v) {
									partial.push(quote(k) + (gap ? ': ' : ':') + v);
								}
							}
						}
					} else {
						for (k in value) {
							if (Object.hasOwnProperty.call(value, k)) {
								v = str(k, value);
								if (v) {
									partial.push(quote(k) + (gap ? ': ' : ':') + v);
								}
							}
						}
					}
					v = partial.length === 0 ? '{}' :
						gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
						mind + '}' : '{' + partial.join(',') + '}';
					gap = mind;
					return v;
			}
		}

		return {
			stringify: function(value, replacer, space) {
				var i;
				gap = '';
				indent = '';

				if (typeof space === 'number') {
					for (i = 0; i < space; i += 1) {
						indent += ' ';
					}
				} else if (typeof space === 'string') {
					indent = space;
				}

				rep = replacer;
				if (replacer && typeof replacer !== 'function' &&
				(typeof replacer !== 'object' ||
				 typeof replacer.length !== 'number')) {
					throw new Error('JSON.stringify');
				}
				return str('', { '': value });
			},

			parse: function(text, reviver) {
				var j;

				function walk(holder, key) {
					var k, v, value = holder[key];
					if (value && typeof value === 'object') {
						for (k in value) {
							if (Object.hasOwnProperty.call(value, k)) {
								v = walk(value, k);
								if (v !== undefined) {
									value[k] = v;
								} else {
									delete value[k];
								}
							}
						}
					}
					return reviver.call(holder, key, value);
				}

				cx.lastIndex = 0;
				if (cx.test(text)) {
					text = text.replace(cx, function(a) {
						return '\\u' + ('0000' +
						(+(a.charCodeAt(0))).toString(16)).slice(-4);
					});
				}

				if (/^[\],:{}\s]*$/.
					test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
					replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
					replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

					j = eval('(' + text + ')');

					return typeof reviver === 'function' ?
				walk({ '': j }, '') : j;
				}
				throw new SyntaxError('JSON.parse');
			}
		};
	} ();

	$.extend($, {

		uid: function(pre) {
			var p = pre || _pre;
			return p + '_' + (_uid++)
		},

		stamp: function(obj) {
			var uid = (typeof (obj) == 'string') ? obj : obj._uid;
			if (!uid) {
				uid = $.uid();
				obj._uid = uid;
			}
			return uid;
		},

		delegate: function(fn, scope, args) {
			return function() { return fn.apply(scope, $.makeArray(args).concat($.makeArray(arguments))); }
		},

		// Adds args into the second parameter of the event call (use for binding to jquery events [event, args])
		delegateEvent: function(fn, scope, args) {
			return function() { return fn.call(scope, arguments[0], $.extend(arguments[1] || {}, args || {})); }
		},

		formatDate: function(value, format) {
			if (typeof (value) == "string" && value.indexOf("/Date(") == 0) {
				var x = value.substring(6, value.length - 2);
				x = parseInt(x, 10);
				value = new Date(x);
			}
			if (!format)
				format = "dd-M-yy";
			return $.datepicker.formatDate(format, value);
		},

		parseDate: function(value, format) {
			if (value && value.getDay)
				return value;
			if (typeof (value) == "string" && value.indexOf("/Date(") == 0) {
				var x = value.substring(6, value.length - 2);
				x = parseInt(x, 10);
				return new Date(x);
			}
			if (format) {
				try {
					return $.datepicker.parseDate(format, value);
				} catch (ex) {
					return null;
				}
			} else {
				var result;
				result = $.parseDate(value, "dd/mm/yy");
				if (result) return result;
				result = $.parseDate(value, "dd/mm/y");
				if (result) return result;
				result = $.parseDate(value, "dd-M-yy");
				if (result) return result;
				result = $.parseDate(value, "dd-M-y");
				if (result) return result;
				return null;
			}
		},

		formatHeader: function(dataField, header) {
			if (header) return header;
			if (!dataField) return '';
			var header = "";
			var upper = null;
			for (var i = 0; i < dataField.length; i++) {
				var c = dataField.substr(i, 1);
				if (!upper && (c === c.toUpperCase()) && header != "") header += " ";
				header += c;
				upper = (c === c.toUpperCase());
			}
			header = header.replace(" Id", "");
			return header;
		},

		formatNumber: function(value, precision) {
			if (typeof (value) !== "number" || !isFinite(value)) return "";
			var s = value.toString();
			var i = s.indexOf('.');
			if (precision > 0 && i < 0) {
				i = s.length;
				s += '.';
			}
			while (i + precision + 1 > s.length) {
				s += '0';
			}
			return s;
		},

		encrypt: function(data) {
			if (data === undefined) return "";
			var json = $.toJson(data);
			var code = "";
			for (var i = 0; i < json.length; i++) {
				code += json.charCodeAt(i).toString(16);
			}
			return code;
		},

		decrypt: function(code) {
			if (code === "") return undefined;
			var json = "";
			for (var i = 0; i < code.length; i += 2) {
				var tmp = code.substr(i, 2);
				json += String.fromCharCode(parseInt(tmp, 16));
			}
			try {
				return $.fromJson(json);
			} catch (ex) {
				$PageManager.redirectTo("/permissiondenied.aspx");
			}
		},

		// Evaluates a client template using the given evaluation function
		evaluateTemplate: function(template, evaluate) {
			var s = 0, e = 0, result = '';
			if (!template)
				return '';
			do {
				// Find the start of the next binding expression
				if ((s = template.indexOf('##', e)) == -1)
					break;
				// Append from previous end to start
				if (s > e)
					result += template.slice(e, s)
				// Find the end of the next binding expression
				if ((e = template.indexOf('##', s + 2)) == -1)
					break;
				// Extract the binding and evaluate it
				var binding = template.slice(s + 2, e);
				result += evaluate(binding);
				// Move end on past the previous binding marker
				e += 2;
			} while (true);
			if (e < template.length)
				result += template.slice(e);
			return result;
		},

		stopPropagation: function(event) {
			event = event || window.event;
			if (!event) return;
			if (event.preventDefault) {
				event.preventDefault();
			} else if ('cancelBubble' in event) {
				event.cancelBubble = true;
			}
		},

		fixAjaxObject: function(o) {
			if (!o) return o;
			for (var key in o) {
				if (typeof (o[key]) == "string" && o[key].indexOf("/Date(") == 0) {
					var x = o[key].substring(6, o[key].length - 2);
					x = parseInt(x, 10);
					o[key] = new Date(x);
				}
			}
			return o;
		},

		toJson: function(value) {
			return Json.stringify(value);
		},

		fromJson: function(value) {
			return Json.parse(value);
		},

		// Pulls settings out of an html element into the settings object
		inlineSettings: function(element, prefix, settings) {
			for (var name in settings) {
				var value = element.attr(prefix + ":" + name);
				if (value) {
					try {
						settings[name] = eval(value);
					} catch (err) {
						settings[name] = value;
					}
				}
			}
		},

		// PUT INTO PAGE MANAGER? PAGE MANAGER WRAPS?
		// Finq Nodes are used to control selection of entities from the server
		finqNodeTypes: {
			Null: 0,
			Equal: 1,
			NotEqual: 2,
			Like: 3,
			GreaterThanOrEqual: 4,
			LessThanOrEqual: 5,
			GreaterThan: 6,
			LessThan: 7,
			OrderBy: 8,
			Select: 9,
			ForeignKey: 10,
			From: 11,
			Page: 12
		},

		finqNode: function(term1, term2, nodeType) {
			if (term1 === undefined || term2 === undefined || nodeType === undefined)
				alert("Invalid finq node (" + term1 + ", " + term2 + ", " + nodeType + ")");
			return { Term1: term1, Term2: term2, NodeType: nodeType };
		},

		call: function(s) {
			s._success = s.success;
			s.beforeSend = function(xhr) { xhr.setRequestHeader("Content-type", "application/json; charset=utf-8"); };
			s.type = s.type || 'POST';
			s.data = $.toJson(s.data || {});
			s.contentType = s.contentType || "application/json; charset=utf-8";
			s.dataType = s.dataType || "json";
			s.error = s.error || function(xhr) {
				if (!xhr.responseText || xhr.responseText.length == 0) {
					alert("Communication link with the server has been lost.  Please try again or refresh the page.");
				} else {
					var error = $.fromJson(xhr.responseText);
					if (!error || !error.Message || error.Message.length == 0) {
						alert("Communication link with the server has been lost.  Please try again or refresh the page.");
					} else {
						// Parse the error message for instructions
						var parts = error.Message.split('|');
						if (parts.length == 1) {
							if (error.Message.indexOf("The DELETE statement conflicted with the REFERENCE constraint") == 0) {
								alert("Unable to delete this record as it is being used by other records in the database.  You must remove all references to this record before you can delete it.");
							} else if (error.Message.indexOf("The DELETE statement conflicted with the SAME TABLE REFERENCE constraint") == 0) {
								alert("Unable to delete this record as it is being used by other records in the same table.  You must remove all references to this record before you can delete it.");
							} else if (error.Message.indexOf("Violation of UNIQUE KEY constraint") == 0) {
								alert("Unable to update this record as it is duplicating a unique field in a record that already exists.");
							} else {
								alert(error.Message);
							}
						} else {
							// For now we just look for a redirect in the first part
							var bits = parts[0].split(':');
							if (bits.length == 2 && bits[0] == 'REDIRECT') {
								$.redirectTo(bits[1]);
								return;
							}
						}
					}
				}
				// TODOL WRAP THIS IN THE PAGE MANAGER METHOD?
				if (window.$PageManager)
					$PageManager.modalUnlock(null, true);
			};
			// TODO: WRAP THIS IN THE PAGE MANAGER METHOD?
			s.success = function(xhr) {
				xhr.d = $.fixAjaxObject(xhr.d);
				if (s._success) s._success(xhr);
				if (s.modal && window.$PageManager) $PageManager.modalUnlock();
			};
			if (s.modal && window.$PageManager) $PageManager.modalLock(s.modal);
			$.ajax(s);
		},

		redirectTo: function(url, data) {
			try {
				if (data !== undefined) {
					data = $.encrypt(data);
					window.location = url + "?$=" + data;
				} else {
					window.location = url;
				}
			}
			catch (e) {
				alert("redirectTo(): Unable to redirect to url '" + url + "'");
			}
		},

		modalLock: function(options) {
			if (!_modalElement) {
				if (!(_modalElement = document.getElementById("_modalElement")))
					return;
				_modalElement = $(_modalElement);
				_modalElement.lock = 0;
			}
			options = options || {};
			if (_modalElement.lock == 0)
				_modalElement.removeClass().addClass("fui-modal fui-modal-" + options.modalClass || "lock").fadeTo(options.modalSpeed === undefined ? 'slow' : options.modalSpeed, options.modalOpacity === undefined ? 0.6 : options.modalOpacity);
			if (options.modalCallback)
				_modalElement.mousedown(options.modalCallback);
			_modalElement.lock++;
			$(_modalElement).show();

			_modalElement.width($(window).width());
			_modalElement.height($(window).height());

			if (options.modalInterval) {
				_modalElement.lock++;
				setTimeout($.modalUnlock, options.modalInterval);
			}
		},

		modalUnlock: function(unlock) {
			if (!_modalElement)
				return;
			_modalElement.lock--;
			if (_modalElement.lock <= 0 || unlock) {
				_modalElement.stop().fadeTo('Fast', 0.0).hide().unbind('mousedown');
				_modalElement.lock = 0;
			}
		},

		// Call as part of the page setup to create the modal element
		modalLocking: function() {
			document.write("<div id='_modalElement'></div>");
			$.modalLock({ modalClass: "loading", modalInterval: 1000, modalOpacity: 1.0, modalSpeed: 0 });
		},

		// THIS NEEDS RETHINKING!!  WE NEED TOTALLY NEW MENU SOLUTION!! Fsn.Menu
		popup: function(options) {
			if (!_popupElement) {
				// Create the global modal popup element
				_popupElement = $("<div class='fui-popup'></div>").appendTo(document.body).hide();
			}
			if (_popupOptions !== null) {
				$.hidePopup();
			}
			options = options || {};
			$.extend(_popupOptions = {}, options);

			if (options.target) {
				var position = options.target.offset();
				var width = options.target.width();
				var height = options.target.outerHeight();
				_popupElement.css("left", position.left);
				_popupElement.css("top", position.top + height);
				_popupElement.width(options.width || width);
			}

			if (options.modal !== false) {
				$.modalLock({ modalClass: "lock", modalOpacity: 0, modalSpeed: 0, modalCallback: $.hidePopup });
			}
			options.callback(_popupElement);
			_popupElement.show();
		},

		hidePopup: function() {
			if (_popupOptions === null)
				return;
			_popupElement.hide().empty().removeClass().addClass("fui-popup");
			if (_popupOptions.model !== false) {
				$.modalUnlock();
			}
			var closed = _popupOptions.closed;
			_popupOptions = null;
			if (closed)
				closed();

		},

		popupMenu: function(options) {
			$.popup({ width: 'auto', callback: $.delegateEvent($._initPopupMenu, $, options.items), target: options.target, modal: true, closed: options.closed });
		},

		_initPopupMenu: function(popupElement, items) {
			popupElement.addClass("fui-popupmenu");
			var ul = $("<ul></ul>").appendTo(popupElement);
			for (var i in items) {
				if (items[i].text) {
					var li = $("<li><a href='#'>" + items[i].text + "</a></li>").appendTo(ul);
					li.click($.delegate(this._clickPopupMenu, this, items[i]));
				}
			}
		},

		_clickPopupMenu: function(item) {
			$.hidePopup();
			if (item.callback) if (item.callback(item) === false) return;
			if (item.url) $PageManager.redirectTo(item.url);
		},

		// PUT INTO PAGE MANAGER? ONLY NEEDED BECAUSE OF RADIO FIELDS? RETHINK FIELDS?
		validateFields: function(group, init) {
			var values = {}; $.extend(values, init || {}); $.validationError = null;
			$PageManager.$fields().field("getValues", values, group);
			$PageManager.$fields().field("validateRadioFields", values, group);
			if ($.validationError) {
				alert($.validationError); return;
			}
			return values;
		},

		// PUT INTO PAGE MANAGER?
		warnOnLeave: function() {
			var fields = [];
			$PageManager.$fields().field("unsavedChanges", fields);
			if (fields.length == 0) return;
			return "You have unsaved changes.";
		}
	});

})(jQuery);


(function($) {

	// Nice little section widget (does expandable / collapsable sections)
	$.widget("fui.section", {
		_init: function() {
			$(".exp", this.element).click($.delegate(this.expand, this)).hide();
			$(".col", this.element).click($.delegate(this.collapse, this)).show();
		},
		expand: function(event, args) {
			$(".exp", this.element).hide();
			$(".col", this.element).show();
			$(".bdy", this.element).slideDown(1000);
		},
		collapse: function(event, args) {
			$(".exp", this.element).show();
			$(".col", this.element).hide();
			$(".bdy", this.element).slideUp(1000);
		}
	});

	// Handy little splitter widget (lets us divide a region into two)
	$.widget("fui.splitter", {
		_init: function() {
			var self = this;
			this.element.children(".fui-splitter-grip").draggable({
				axis: 'x',
				helper: 'clone',
				opacity: 0.5,
				//containment: [100, 0, self.element.width() - 100, 0],
				stop: function(event, ui) {
					// Bound the grip to within +/- 100px of either edge
					var width = self.element.width();
					var left = ui.position.left;

					if (left < 100) left = 100;
					if (left > width - 100) left = width - 100;
					if (left < 100) return; // container is < 200px wide!

					var position = (left * 100 / width) + "%";
					self.element.children(".fui-splitter-content1").width(position);
					self.element.children(".fui-splitter-grip").css("left", position);
					//self.element.children(".fui-splitter-content2").css("margin-left", position);
				}
			}).mousedown(function(event) {
				event.preventDefault();
			});
		}
	});
	$.extend($.fui.splitter, {
		defaults: {
	}
});

// This is a LavaLamp style main menu
$.widget("fui.menu", {
	_init: function() {
		var self = this;
		var rootUL = $("ul.root", this.element);
		$("li.root", this.element).hover($.delegateEvent(this._move, this), function() { $("ul", this).hide(); }).mouseup($.delegateEvent(this._mouseup, this));
		this.options.back = $("<li class='sel'> </li>").appendTo(rootUL).hide();
		var rootLIs = $("li.root");
		if (rootLIs && rootLIs.length > 0) {
			this.options.base = rootLIs[0];
			for (var i = 0; i < rootLIs.length; i++) {
				if (rootLIs[i].innerHTML.indexOf(this.options.menuBase) == 0)
					this.options.base = rootLIs[i];
			}
		}
		this.element.hover(null, function() { self._moveTo(self.options.base); $("ul ul", self.element).hide(); });
		this._moveTo(this.options.base);
		// Sub-menus
		$("ul ul").hide();
		// Page URLs
		$("li", this.element).mouseup(function() { if ($(this).attr("menu:pageurl")) { $PageManager.redirectTo($(this).attr("menu:pageurl")); } });
		// Expand/Collapse Buttons
		$(".fui-menu .menu-v").click(function() {
			var $icon = $(".fui-menu .menu-v");
			if ($icon.hasClass("ui-icon-collapse-v")) {
				$icon.removeClass("ui-icon-collapse-v").addClass("ui-icon-expand-v");
				$(".banner").hide();
				//$(".title").hide();
				$("div.header").height("65px");
				$("div.footer").height("0px").hide();
				$(".content").height($(".content").parent().height() - $("div.header").height() - $("div.footer").height());
			} else {
				$icon.removeClass("ui-icon-expand-v").addClass("ui-icon-collapse-v");
				$(".banner").show();
				//$(".title").show();
				$("div.header").height("110px");
				$("div.footer").height("40px").show();
				$(".content").height($(".content").parent().height() - $("div.header").height() - $("div.footer").height());
			}
		});
		$(".fui-menu .menu-h").click(function() {
			var $icon = $(".fui-menu .menu-h");
			if ($icon.hasClass("ui-icon-collapse-h")) {
				$icon.removeClass("ui-icon-collapse-h").addClass("ui-icon-expand-h");
				$("div.wrapper").width('980px');
			} else {
				$icon.removeClass("ui-icon-expand-h").addClass("ui-icon-collapse-h");
				$("div.wrapper").width('100%');
			}
		});
		$(".fui-menu .menu-ea").click(function() {
			$(".fui-section").section("expand");
		});
		$(".fui-menu .menu-ca").click(function() {
			$(".fui-section").section("collapse");
		});

	},
	destroy: function() {
	},
	_mouseup: function(event) {
	},
	_move: function(event) {
		//event.stopPropagation();
		var target = event.target;
		while (!$(target).hasClass("fui-menu")) {
			if (target.nodeName == 'LI' && $(target).hasClass("root")) break;
			target = target.parentNode;
		}
		if ($(target).hasClass("fui-menu")) return;
		this._moveTo(target);
	},
	_moveTo: function(target) {
		if (!target) return;
		this.options.back.each(function() {
			$.dequeue(this, "fx");
		}
			).animate({
				top: target.offsetTop,
				width: target.offsetWidth,
				left: $(target).offset().left
			}, 'normal', 'swing');
		// Show a sub menu
		$("ul", target).show().css("left", $(target).offset().left).css("top", $(target).height() + 7);
	},
	_closed: function() {
	}
});
$.extend($.fui.menu, {
	menuBase: ""
});

// Very simple tab/panel widgets

$.widget("fui.tab", {
	_init: function() {
		var self = this;
		// Look for inline settings
		for (var attrName in $.fui.tab.defaults) {
			var attrValue = this.element.attr("tab:" + attrName);
			if (attrValue) {
				try {
					this.options[attrName] = eval(attrValue);
				} catch (err) {
					this.options[attrName] = attrValue;
				}
			}
		}
		this.element.mouseup(function(event) {
			$PageManager.$tabs().tab("select", self.options.name, self.options.group);
			$PageManager.$panels().panel("select", self.options.name, self.options.group);
		});
	},

	select: function(name, group) {
		if (this.options.group != group) return;
		if (this.options.name == name) this.element.addClass("sel");
		else this.element.removeClass("sel");
	}
});
$.extend($.fui.tab, {
	defaults: {
		name: "",
		group: ""
	}
});

$.widget("fui.panel", {
	_init: function() {
		// Look for inline settings
		for (var attrName in $.fui.panel.defaults) {
			var attrValue = this.element.attr("panel:" + attrName);
			if (attrValue) {
				try {
					this.options[attrName] = eval(attrValue);
				} catch (err) {
					this.options[attrName] = attrValue;
				}
			}
		}
	},
	select: function(name, group) {
		if (this.options.group != group) return;
		if (this.options.name == name) this.element.addClass("sel");
		else this.element.removeClass("sel");
	}
});
$.extend($.fui.panel, {
	defaults: {
		name: "",
		group: ""
	}
});


})(jQuery);

