/**
 * Shortcut for document.getElementById()
 * @param {String} id
 * @returns	{HTMLDOMElement}
 */
function gel(id) {
	return document.getElementById(id);
}

var MG = (function() {
	var bootstraps = [];
	
	var debug = false;
	var seed = new Date().getTime();
	/**
	 * Generates a GUID for the UI session.
	 * @function
	 * @name	MG.generateGUID
	 * @returns	{String} GUID
	 */
	function generateGUID() {
		return new String(seed++);
	}

	/**
	 * Statically constructs JS namespacing in a predicatable manner.
	 * Root namespace "MG" is implied in all calls.
	 * 
	 * @param	{*String}	ns String or array of Strings with dot-notation paths to namespace(s)
	 * @return	{Object}	reference to namespace, i.e, fleshed out MG.
	 */
	function namespace() {
		var i,n,n2,j,ns,root,
			a = arguments;
			
		for(i=0, n=a.length; i<n; i++) {
			ns = a[i].split('.');
			if(MG.env.debug === true) MG.log("@namespace - building for " + ns);
			root = MG;
			
			for(j=(ns[0] == "MG" ? 1 : 0), n2=ns.length; j<n2; j++) {
				root[ns[j]] = root[ns[j]] || {};
				root=root[ns[j]];
			}
		}
		return root;
	}

	/**
	 * Determines if the passed variable is a javascript String object
	 * @name MG.isString
	 * @function
	 * @param {Object} s
	 * @returns {boolean}
	 */
	function isString(s) {
		if(s)
			return s.constructor == String;
		return false;
	}
	/**
	 * @static
	 * @name gelEl
	 * @member MG
	 */
	function getEl(id) {
		if(isString(id)) {
			return gel(id);
		}
		
		if(id.id == "") {
			id.id = "MG_gen_" + generateGUID();
		}
		
		return id;
	}

	/**
	 * IE/Safari/Opera safe log
	 */
	var logPrepend = "MG:";
	
	function _log(type, args) {
		if(typeof(console) != "undefined") {
			try {
				if(args.length === 1)
					console[type](logPrepend + args[0]);
				else
					console[type](logPrepend, args);

				if(type == 'log' && MG.env.debug === true) {
					//console.trace();
				}
			} catch(e) {}
		}
	}
	function log() {
		_log.call(this, 'log', arguments);
	}
	function warn() {
		_log.call(this, 'warn', arguments);
	}
	function info() {
		_log.call(this, 'info', arguments);
	}
	function error() {
		_log.call(this, 'error', arguments);
	}
	function trace() {
		_log.call(this, 'error', arguments);
	}
	
	/**
	 * Template utility class that is propagated to all classes extending MG
	 * 
	 * @example (Standard):
	 * ------------------------------------------------------------------------
	 * this example generates a simple string with a custom value.
	 * 
	 *     var t = new MG.util.Template("&lt;p&gt;this string is applied dynamically: [ {templateTarget} ]&lt;\p&gt;");
	 *     t.applyValues({
	 * 		    templateTarget : 'this replaces the "templateTarget" target'
	 *     });
	 * 
	 *     t.append("someDomId");
	 * or 
	 *     t.append(el); // el being a dom object
	 * 
	 * The above will result in a string being inserted at someDomId or after el:
	 * 
	 *     &lt;p>this string is applied dynamically: [ this replaces the "templateTarget" target ]&lt;/p>
	 * 
	 * 
	 * 
	 * @example (Iterator):
	 * ------------------------------------------------------------------------
	 * // this example generates a <ul> with 2 items with custom values 
	 * var template = "&lt;ul>" +
	 * 		"{{&lt;li>this will repeat : {item}&lt;/li>}}" + 
	 * "&lt;/ul>";
	 * 
	 * var t = new MG.util.Template(template);
	 * t.applyIterator([
	 * 		{
	 * 			item : 'value 1'
	 * 		},{
	 * 			item : 'value 2'
	 * 		}
	 * ]);
	 * t.append("someDomId");
	 * 
	 * @property {String} snippet
	 * @return {Object} Template instance
	 * 
	 * @member MG.util
	 * @constructor
	 * @class 
	 */
	function Template(snippet) {
		// explicit privates
		var templateSnippet, originalSnippet, tempNode, el,
			tempNodeId = "__MG_temp_node";
		
		// internal closures
		templateSnippet = snippet;
		originalSnippet = snippet;
		tempNode = document.createElement("div");
		tempNode.id = tempNodeId;
		
		/**
		 * Cleans the template of replacement targets, i.e., {replace.me}
		 * @private
		 * @member MG.util.Template
		 */
		function cleanTemplate() {
			templateSnippet = templateSnippet.replace(/{[a-z0-9\_\-]+}/gim, "");//.replace(/{{[^}]{2}}}/gim, "");
		}

		/**
		 * Set the markup snippet on the fly
		 * @param	{String}	snippet	HTML markup with template targets
		 */		
		this.set = function(snippet) {
			templateSnippet = snippet;
			originalSnippet = snippet;
		};
		

		/**
		 * Appends snippet as DOM to the targeted element
		 * @param {String|HTMLDOMElement} id Id of target element or element reference
		 * @return {HTMLDOMElement|null} the DOM reference of the new template inserted
		 */
		this.append = function(id) {
			el = getEl(id);
			if(el) {
				cleanTemplate();
				tempNode.innerHTML = templateSnippet;
				for(var i=0, n=tempNode.childNodes.length; i<n; i++) {
					// index 0 as we append, it moves the DOM el
					el.appendChild(tempNode.childNodes[0]);
				}
				
				return el.childNodes[el.childNodes.length - 1];
			}
			return null;
		};
		/**
		 * Inserts snippet as DOM as the first child into the targeted element
		 * @param {String|HTMLDOMElement} id Id of target element or element reference
		 * @return {HTMLDOMElement|null} the DOM reference of the new template inserted
		 */
		this.insert = function(id) {
			el = getEl(id);
			if(el) {
				cleanTemplate();
				tempNode.innerHTML = templateSnippet;

				for(var n=tempNode.childNodes.length - 1; n >= 0; n--) {
					el.insertBefore(tempNode.childNodes[n], el.firstChild);
				}

				return el.firstChild;
			}
			return null;
		};
		
		/**
		 * Inserts snippet as DOM after the element passed in.
		 * @param {String|HTMLDOMElement} id
		 */
		this.insertAfter = function(id) {
			el = getEl(id);
			if(el) {
				cleanTemplate();
				tempNode.innerHTML = templateSnippet;

				for(var n=tempNode.childNodes.length - 1; n >= 0; n--) {
					el.parentNode.insertBefore(tempNode.childNodes[n], el.nextSibling);
				}

				return el.nextSibling;
			}
			return null;
		};
		
		
		/**
		 * Applies the values for macro substitution against the template snippet.
		 * The sole argument is a plain Javascript object as associative array.
		 * @param {Object} obj Associate array of key/value pairs to apply to the template snippet
		 * @return void
		 */
		this.applyValues = function(obj) {
			templateSnippet = originalSnippet;
			for(var k in obj) {
				if(isString(obj[k])) {
					
					templateSnippet = templateSnippet.replace(new RegExp('{'+k+'}', "gm"), obj[k]);
				}
			}
		};
		/**
		 * Applies the values for macro substitution using an iterator against the template snippet.
		 * The sole argument is a plain Javascript object as associative array.
		 * @param	{Array}	a	Standard array populated with Objects-as-hash key/value pairs to apply
		 *						against the template using {{ {target} }} markup
		 * @return void
		 */
		this.applyIterator = function(a) {
			templateSnippet = originalSnippet;
			var i, n, k, dom, s, s1,
				finishedChunks = [],
				tmp = templateSnippet,
				rx = /{{(.+)}}/m,
				iteratorSnippet = tmp.match(rx);
			
			if(iteratorSnippet.length == 2) {
				s = iteratorSnippet[1];
				for(i=0, n=a.length; i<n; i++) {
					s1 = s;
					for(k in a[i]) {
						if(MG.isString(a[i][k]))
							s1 = s1.replace(new RegExp('{'+k+'}', "gm"), a[i][k]);
					}
					finishedChunks.push(s1);
				}
				templateSnippet = tmp.replace(rx, finishedChunks.join(""));
			}
		};
		
		/**
		 * Returns the compiled templateSnippet for further processing, manual 
		 * attachment, serialization, etc.
		 * @return	{String}	templateSnippet
		 */
		this.toString = function() {
			cleanTemplate();
			return templateSnippet;
		};
	}
	
	function closeModal() {
		$(document.body).removeClass("modal-on");
	}
	function toggleModal() {
		var b = $(document.body).toggleClass("modal-on");
		$("#modal").css("height", $(document).height() + "px");
	}
	
	/**
	 * Adjusts the center area to show all content
	 */
	function fixContentHeight() {
		var h = 0;
		$(".popped-front-body .content").each(function() {
			h += $(this).height();
		});
		
		$(".popped-front-body").height(h + 50);
	}
	
	/**
	 * Adjusts center content to show site without scrollbars
	 */
	function fixContentHeightNoScroll() {
		var viewport = $(window).height();
		var floorHeight;
		
		viewport -= $(".header-tip").height();
		viewport -= $(".header-front").height();
		viewport -= $(".footer-front").height();
		viewport -= $(".footer-tip").height();
		
		viewport -= 40;
		floorHeight = viewport;
		
		//$(".main-content").height(viewport);
		$(".popped-front-body").css("min-height", "300px").css("height", "auto").css("overflow", "auto");
		$(".popped-front-body").height(viewport);
		
		$(window).unbind("resize").bind("resize", fixContentHeightNoScroll);
	}
	
	/**
	 * Registers callbacks to call when bootstraping core
	 * @param {Function} cb
	 */
	function registerBoot(cb) {
		bootstraps.push(cb);
	}
	
	/**
	 * Stars any processes on the page.
	 */
	function bootstrap() {
		if(MG.env.noScroll === true) {
			fixContentHeightNoScroll();
		} else if(MG.env.fixedHeight === false) {
			fixContentHeight();
		}
		
		$(document).bind("keyup", function(e) {
			if(e.keyCode === 27) {
				closeModal();
			}
		});
		
		$(".humane-date").each(function() {
			var d = this.innerHTML;
			d = d.replace(/-/g, '/');
			var theDate = new Date(d);
			this.innerHTML = humaneDate(theDate);
		});
		
		for(var i=0, n=bootstraps.length; i<n; i++) {
			bootstraps[i]();
		}
	}

	function closeGrowl() {
		$('.jGrowl-close, .jGrowl-closer').click();
	}	
	
	var email_re = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/i;
	function isValidEmail(s) {
		MG.log("Validating email address [ " + s + " ]");
		return s.match(email_re);
	}
	
	return {
		bootstrap					: bootstrap,
		closeGrowl					: closeGrowl,
		fixContentHeight			: fixContentHeight,
		fixContentHeightNoScroll	: fixContentHeightNoScroll,
		generateGUID				: generateGUID,
		isValidEmail				: isValidEmail,	
		namespace					: namespace,
		registerBoot				: registerBoot,
		
		/* modals */
		closeModal					: closeModal,
		toggleModal					: toggleModal,
		
		/* logs */
		log							: log,
		warn						: warn,
		error						: error,
		info						: info,
		
		strings : {},
		env : {
			fixedHeight	: false,
			noScroll	: false,
			debug	: debug
		},
		
		util : {
			Template : Template
		},
		widget : {}
	};
})();

$(document).ready(MG.bootstrap);
