﻿/// <reference path="jquery-1.4.1-vsdoc.js" />

if (typeof jQuery == "undefined")
	alert("jQuery is not loaded");
else if (typeof jQuery.fdg == "undefined") {
	(function ($) {

		function Blocker(container, layer) {
			this.container = container;
			this.layer = layer;
		};
		var typeMap = {};

		$.each("Boolean Number String Function Array Date RegExp Object".split(" "), function (i, name) {
			typeMap["[object " + name + "]"] = name.toLowerCase();
		});

		$.extend($, {
			namespace: function (ns, extension) {
				/// <summary>Creates a namespace from $</summary>
				var o = $;
				$.each(ns.split("."), function (i, value) {
					o = o[value] ? o[value] : (o[value] = {
						extend: function (proto) {
							return $.extend.call($, this, proto);
						}
					});
				});

				if ($.type(extension) == "object")
					o.extend(extension);

				return o;
			},
			type: function (obj) {
				/// <summary>Gets the object type</summary>
				return obj == null ? String(obj) : typeMap[Object.prototype.toString.call(obj)] || "object";
			}
		});

		$.namespace("fdg");

		window.StringBuilder = function () {
			/// <summary>Represents a mutable string of characters</summary>
			var _this = this;
			if (arguments.length > 0)
				jQuery.each(arguments, function (i, arg) {
					_this.append(arg);
				});

			this._buffer = [];
		};

		window.StringBuilder.prototype = {
			append: function (str) {
				/// <summary>Appends a copy of the specified string to the end of this instance</summary>
				/// <param name="str" type="String">The String to append. </param>
				if (str.length > 0)
					this._buffer.push(str);
			},
			appendFormat: function (format, args) {
				/// <summary>Appends a formatted string, which contains zero or more format specifications, to this instance. Each format specification is replaced by the string representation of a corresponding object argument.</summary>
				/// <param name="format" type="String">A composite format string</param>
				/// <param name="args" type="Object" parameterArray="true">(Optional) An array of objects to format</param>
				this.append(String.format.apply(String, arguments));
			},
			toString: function () {
				return this._buffer.join("");
			}
		}

		$.extend(String, {
			_appendFormatRx: /\{([0-9]+)\}/g,
			format: function (format, args) {
				/// <summary>Returns a formatted string, which contains zero or more format specifications, to this instance. Each format specification is replaced by the string representation of a corresponding object argument.</summary>
				/// <param name="format" type="String">A composite format string</param>
				/// <param name="args" type="Object" parameterArray="true">(Optional) An array of objects to format</param>
				var _arguments = arguments;
				return format.replace(this._appendFormatRx, function (m, i) {
					return _arguments[parseInt(i) + 1].toString();
				});
			},
			isNullOrEmpty: function (str) {
				/// <summary>Checks to see if the passed string is null or contains and empty string</summary>
				if (!str) return true;
				if (str.length == 0) return true;
				return false;
			}
		});

		$.extend(String.prototype, {
			trim: function () {
				/// <summary>Trims whitespace from the start and end of the current string</summary>
				return this.replace(/(^\s+)|(\s+$)/g, "");
			},
			trimStart: function () {
				/// <summary>Trims whitespace from the start of the current string</summary>
				return this.replace(/^\s+/g, "");
			},
			trimEnd: function () {
				/// <summary>Trims whitespace from the start of the current string</summary>
				return this.replace(/\s+$/g, "");
			},
			startsWith: function (s, ignoreCase) {
				/// <summary>returns true if String starts with s</summary>
				var thisStr = ignoreCase ? this.toLowerCase() : this;
				s = ignoreCase ? s.toLowerCase() : s;
				if (ignoreCase)
					return thisStr.startsWith(s, false);
				return this.length >= s.length && this.substring(0, s.length) == s;
			},
			contains: function (str, ignoreCase) { var thisStr = ignoreCase ? this.toLowerCase() : this; str = ignoreCase ? str.toLowerCase() : str; if (ignoreCase) return thisStr.contains(str, false); return this.indexOf(str) >= 0; }
		});

		$.extend(Array.prototype, {
			removeAt: function (index) {
				var ret = this[index];
				for (var i = index; i < this.length - 1; i++)
					this[i] = this[i + 1];
				this.pop();
				return ret;
			},
			clear: function () {
				this.splice(0, this.length);
			}
		});

		$.namespace("fdg.ui", {
			nextHighestDepth: 10000000,
			getNewHighestDepth: function () { return $.fdg.ui.nextHighestDepth++; },
			layers: [],
			blocker: null,
			blockerContent: null,
			block: function (colour, opacity, useSingle) {
				if (useSingle && this.blocker)
					return this.blocker;
				this.blockerContent = $("<div></div>").appendTo(document.body).css({
					position: "fixed",
					top: 0,
					height: $(window).height(),
					width: $(window).width(),
					zIndex: $.fdg.ui.nextHighestDepth + 2
				});
				this.blocker = this.blockerContent.clone().appendTo(document.body).css({
					background: colour,
					opacity: opacity,
					alpha: opacity.toString(),
					zIndex: $.fdg.ui.getNewHighestDepth()
				});
				$.fdg.ui.getNewHighestDepth();
				this.layers.push(new Blocker(this.blockerContent, this.blocker));
				return this.blockerContent;
			},
			unblock: function () {
				if (!this.blocker)
					return;
				this.blocker.remove();
				this.blockerContent.remove();
				if (this.layers.length > 1) {
					this.layers.pop();
					this.blocker = this.layers[this.layers.length - 1].layer;
					this.blockerContent = this.layers[this.layers.length - 1].container;
				}
				else {
					this.layers.clear();
					this.blocker = null;
					this.blockerContent = null;
				}
			}
		});

		$.namespace("fdg.internal.forms", {
			hintClassName: "hint",

			addHintToControl: function (ctrl, hint) {
				var _this = this;
				ctrl.each(function () {
					if (this.tagName.toLowerCase() == "textarea")
						_this.addHintToTextbox($(this), hint);
					else if (typeof this.type == "string") {
						if (this.type == "text")
							_this.addHintToTextbox($(this), hint);
						else if (this.type == "password")
							_this.addHintToPassword($(this), hint);
						else
							$.fdg.debug.log(String.format("Could not add hint to control type {0}", this.type), "red");
					}
					else
						$.fdg.debug.log(String.format("Could not add hint to element type {0}", this.tagName), "red");
				});
			},

			addMaxlengthToControl: function (ctrl, maxlength) {
				var _this = this;
				ctrl.each(function () {
					var $this = $(this);
					if (this.tagName.toLowerCase() == "textarea")
						_this.addMaxlengthToTextArea($this, maxlength);
					else if (typeof this.type == "string") {
						if (this.type == "text" || this.type == "password")
							$this.attr("maxlength", maxlength);
						else
							$.fdg.debug.log(String.format("Could not add maxlength to control type {0}", this.type), "red");
					}
					else
						$.fdg.debug.log(String.format("Could not add maxlength to element type {0}", this.tagName), "red");
				});
			},

			addHintToTextbox: function (textbox, hint) {
				var _this = this;
				textbox.attr("hint", hint).focus(function () {
					var $this = $(this);
					if ($this.val().trim() == $this.attr("hint"))
						$this.val("").removeClass(_this.hintClassName);
				}).blur(function () {
					var $this = $(this);
					if (String.isNullOrEmpty($this.val().trim()))
						$this.val($this.attr("hint")).addClass(_this.hintClassName);
				}).blur();
			},

			addHintToPassword: function (textbox, hint) {
				textbox.attr("hint", hint).data("relatedTextbox",
					$("<input type=\"text\" />").val(hint).css("display", "none").insertBefore(textbox).addClass(textbox[0].className).addClass(this.hintClassName).focus(function () {
						$(this).css("display", "none");
						textbox.css("display", "").focus();
					})
				).blur(function () {
					var $this = $(this);
					if (String.isNullOrEmpty($this.val().trim()))
						$this.css("display", "none").data("relatedTextbox").css("display", "").val($this.attr("hint"));
				}).blur();
			},

			addMaxlengthToTextArea: function (textbox, maxlength) {
				textbox.data("maxlength", maxlength).data("changed", function () {
					this.val(this.val().substring(0, this.data("maxlength")));
				}).keyup(function () {
					var $this = $(this);
					$this.data("changed").call($this);
				}).keydown(function () {
					var $this = $(this);
					$this.data("changed").call($this);
				});
			}
		});

		$.namespace("fdg.debug", {
			assert: function () {
				///	<summary>Debug library not loaded</summary>
			},
			fail: function () {
				///	<summary>Debug library not loaded</summary>
			},
			log: function () {
				///	<summary>Debug library not loaded</summary>
			},
			hasMember: function () {
				///	<summary>Debug library not loaded</summary>
			},
			assertMember: function () {
				///	<summary>Debug library not loaded</summary>
			},
			ensureMember: function () {
				///	<summary>Debug library not loaded</summary>
			}
		});

		$.namespace("fdg.forms", {
			extendControl: function (ctrl, options) {
				/// <summary>Adds additional functionality to the built-in form elements</summary>
				/// <param name="ctrl" type="jQuery">A jQuery object containing one or more elements</param>
				/// <param name="options" type="Object">
				///		An object containing key/value pairs for the extensions to add. These
				///		extensions can be any number of the following: hint (String), maxlength (Int)
				///	</param>
				$.each(options, function (key, value) {
					switch (key.toLowerCase()) {
						case "hint":
							$.fdg.internal.forms.addHintToControl(ctrl, value);
							break;
						case "maxlength":
							$.fdg.internal.forms.addMaxlengthToControl(ctrl, parseInt(value));
							break;
						default:
							$.fdg.debug.log(String.format("Unknown control extension '{0}'", key), "red");
					}
				});
			}
		});

	})(jQuery);
}
