Monitus UI Library

util  4

Monitus UI Library > util > jmui_core.js (source view)
Search:
 
Filters
/*!
 * jjMUI - Monitus UserInterface Javascript Library
 * Copyright (c) 2011-2012 Monitus, LLC - www.monitus.com
 */

var JSON = JSON || {};
JSON.stringify = JSON.stringify || function(obj) {
	var t = typeof(obj);
	if(t != "object" || obj === null) {
		if(t == "string") obj = '"'+obj+'"';
		return String(obj);
	} else {
		var n, v, json = [], arr = (obj && obj.constructor == Array);
		for(n in obj) {
			v = obj[n]; t = typeof(v);
			if(t == "function") continue;
			if(t == "string") v = '"'+v+'"';
			else if(t == "object" && v !== null) v = JSON.stringify(v);
			json.push((arr ? "" : '"' + n + '":') + String(v));
		}
		return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
	}
};
JSON.parse = JSON.parse || function(str) {
	if(str === "") str = '""';
	eval("var p=" + str + ";");
	return p;
};
if(!Array.prototype.indexOf) {
	Array.prototype.indexOf = function(needle, start) {
		start = start || 0;
		for(var loop = start; loop < this.length; loop++) {
			if(this[loop] === needle) { return loop; }
		}
		return -1;
	}
}

var jMUI = jMUI || {_version: 1, ready: false, roger_rabbit: (window != top)};
jMUI.$ = jMUI.$ || null;
jMUI.inits = jMUI.inits || [];
jMUI._waiter = jMUI._waiter || null;
jMUI._waitQueue = jMUI._waitQueue || [];
jMUI._objectQueue = jMUI._objectQueue || {};
jMUI._clocks = jMUI._clocks || {};
jMUI._timers = jMUI._timers || {};
jMUI._min_jquery = jMUI._min_jquery || "1.3.2";
jMUI._default_jquery = jMUI._default_jquery || "1.7.2";
jMUI._protocol = jMUI._protocol || document.location.protocol;
jMUI._https = jMUI._https || ("https:" == jMUI._protocol);

jMUI.internal = jMUI.internal || {};
jMUI.internal._debug = jMUI.internal._debug || function() {
	if(jMUI._test_mode && (typeof(console) != "undefined")) console.log.apply(console, arguments)
};
jMUI.internal._test_object = jMUI.internal._test_object || function(pObjectName) {
	try {
		eval("var vObject = window." + pObjectName + ";");
		return vObject;
	} catch(e) {
		return null;
	}
};
jMUI._waitDone = jMUI._waitDone || function() {
	for(var vLoop = (jMUI._waitQueue.length - 1); vLoop >= 0; vLoop--) {
		if(jMUI._waitQueue[vLoop](true) !== true) jMUI._waitQueue.splice(vLoop, 1); // return true to repeat, false otherwise
	}
	jMUI._waiter = 0;
	if(jMUI._waitQueue.length > 0) jMUI._waiter = setTimeout("jMUI._waitDone();", 250);
};
jMUI.internal._wait = jMUI.internal._wait || function(pFunction) {
	jMUI._waitQueue.unshift(pFunction); // queue in reverese order dso we can execute in the right order
	if(!jMUI._waiter) jMUI._waiter = setTimeout("jMUI._waitDone();", 250);
};
jMUI.internal._wait_object = jMUI.internal._wait_object || function(pObjectName, waiting) {
	var vInfo = jMUI._objectQueue[pObjectName];
	
	var vDebug = "[wait[" + pObjectName;
	if(vInfo) vDebug += "," + vInfo + "," + vInfo.callbacks.length;
	vDebug += "]";
	//jMUI.internal._debug(vDebug);
	
	if(vInfo) {
		var vObject = jMUI.internal._test_object(pObjectName);
		if(!vObject) {
			if(waiting) return (++vInfo.tries <= 20); // avoid flooding the page with callbacks...
			jMUI.internal._wait(function(w){return jMUI.internal._wait_object(pObjectName, w);});
		} else {
			delete jMUI._objectQueue[pObjectName];
			for(var vLoop = 0; vLoop < vInfo.callbacks.length; vLoop++) jMUI.util.wait_object(pObjectName, vInfo.callbacks[vLoop], vInfo.args[vLoop]);
		}
		return false;
	}
};
jMUI.internal._loadImage = jMUI.internal._loadImage || function(pURL, pLoadHandler) {
	if(!pURL) return;
	var vImage = new Image();
	if(pLoadHandler) vImage.onload = pLoadHandler;
	vImage.src = pURL;
};
jMUI.internal._report_error = jMUI.internal._report_error || function(pErrorID, pMessage) { jMUI.internal._loadImage(document.location.protocol + "//www.monitus.net/bin/1519330469/merr.php?e=" + encodeURIComponent(pErrorID) + "&u=" + encodeURIComponent(document.location) + "&m=" + encodeURIComponent(pMessage)); };
jMUI.internal._injectHeadTag = jMUI.internal._injectHeadTag || function(tag, type, url, content, tagID, callback, clock_name) {
	if(url && !url.match(/^(https?\:)?\/\//i)) url = jMUI._protocol + "//" + url;
	// *** DO NOT use jquery here, as we use this code to inject jquery too... !
	var ie = window.navigator.userAgent.match(/MSIE\s(\d)\.\d/i);
	var old_ie = false;
	if(ie) old_ie = (ie && (ie[1] == "6"));
	var header = document.getElementsByTagName((old_ie ? "body" : "head"));
	if(!header || (header.length == 0)) {
		jMUI.internal._debug("WAIT "+(ie ? "body" : "head")+" "+url);
		if(typeof(jMUI.internal._injectHeadTagCounters) == "undefined") jMUI.internal._injectHeadTagCounters = {};
		if(typeof(jMUI.internal._injectHeadTagCounters[url]) == "undefined") jMUI.internal._injectHeadTagCounters[url] = 1;
		else jMUI.internal._injectHeadTagCounters[url]++;
		if(jMUI.internal._injectHeadTagCounters[url] <= 100) setTimeout(function() { jMUI.internal._injectHeadTag(tag, type, url, content, tagID, callback, clock_name); }, 10);
		else jMUI.internal._debug("WAITING "+(ie ? "body" : "head")+" FOREVER: "+url);
	} else {
		header = header[0];
		
		var object = document.createElement(tag);
		if(tagID) object.id = tagID;
		if(tag.match(/style/i)) object.type = "text/css";
		else {
			object.type = type;
			if(tag.match(/link/i)) {
				object.rel = "stylesheet";
				if(url) object.href = url;
			} else if(url) {
				object.src = url;
				object.async = true;
				var loaded = false;
				object.onload = object.onreadystatechange = function() {
					if(!loaded && (!this.readyState || (this.readyState == 'loaded') || (this.readyState == 'complete'))) {
						loaded = true;
						object.onload = object.onreadystatechange = null;
						if(callback) callback();
					};
				};
			} else if(content) {
				if(ie) object.text = content;
				else object.appendChild(document.createTextNode(content));
			}
		}
		if(clock_name) jMUI.util.clock(clock_name);
		header.appendChild(object);
		if(tag.match(/style/i)) {
			if(object.styleSheet) {
				if(object.styleSheet.cssText == "") object.styleSheet.cssText = "";
				object.styleSheet.cssText += content;
			} else object.appendChild(document.createTextNode(content));
		}
	}
};
jMUI.internal._injectScript = jMUI.internal._injectScript || function(pURL, pCallback, clock_name) { // cross domain!
	jMUI.internal._injectHeadTag("script", "text/javascript", pURL, null, null, pCallback, clock_name);
};
jMUI.internal.init = jMUI.internal.init || function() {
	jMUI.$.fn.center = function() {
		this.css("position","absolute");
		var windowObject = $(window);
		this.css("top", Math.max(0, ((windowObject.height() - this.outerHeight()) / 2) + windowObject.scrollTop()) + "px");
		this.css("left", Math.max(0, ((windowObject.width() - this.outerWidth()) / 2) + windowObject.scrollLeft()) + "px");
		return this;
	};
	(function($) {
		function regex(elem, text) {
			var matchParams = text.split(',');
			var validLabels = /^(data|css):/;
			var attr = {
				method: matchParams[0].match(validLabels) ? matchParams[0].split(':')[0] : 'attr',
				property: matchParams.shift().replace(validLabels,'')
			};
			var regexFlags = 'ig';
			var regex = new RegExp(matchParams.join('').replace(/^\s+|\s+$/g,''), regexFlags);
			return regex.test(jMUI.$(elem)[attr.method](attr.property));
		}
		
		$.expr[':'].regex = $.expr.createPseudo ?
			$.expr.createPseudo(function(text) {
				return function(elem) {
					return regex(elem, text);
				};
			}) :
			function(elem, i, match) {
				return regex(elem, match[3]);
			};
	})(jMUI.$);
	
	(function($) {
		$._onInserted = {
			EVENT_NAME: "nodeInserted",
			CSS_ANIM_NAME: "monitusNodeInserted",
			
			css: null,
			observer: null,
			
			css_proprietary: function() {
				// https://github.com/krazyjakee/jQuery-Keyframes
				var ua = navigator.userAgent;
				if(ua.indexOf('Opera') != -1) return '-o-';
				if(ua.indexOf('MSIE') != -1) return '-ms-';
				if(ua.indexOf('WebKit') != -1) return '-webkit-';
				if(navigator.product == 'Gecko') return '-moz-';
				return '';
			},
			
			ie_snitch: function(element, filter) {
				element.style.behavior = "none"; // avoid infinite loop
				$._onInserted.trigger(element, filter);
			},
			
			trigger: function(element, filter) {
				var nodes = $(element);
				if((filter != "*") && !nodes.is(filter)) nodes = nodes.find(filter);
				nodes.trigger($._onInserted.EVENT_NAME);
			}
		};
	
		$.fn.onInserted = function(selector, callback, filter) {
			var self = this;
			if(($.support.mutationObservers || $.support.transition || $.support.mutationEvents) && !$.support.behaviors) { // > IE8; otherwise, no reliable workarounds on older IE versions...
				if(typeof(filter) == "undefined") filter = "*";
			
				var mutations_snitch = function(mutations) {
					mutations.forEach(function(mutation) {
						if(mutation.type.match(/childList|subtree/)) {
							for(var i = 0; i < mutation.addedNodes.length; i++) {
								if(mutation.addedNodes[i].nodeType == 1) $._onInserted.trigger(mutation.addedNodes[i], filter);
							}
						}
					});
				};
				var css_snitch = function(event) {
					if(event.animationName == $._onInserted.CSS_ANIM_NAME) $._onInserted.trigger(event.target, filter);
				};
				var dom_snitch = function(event) {
					$._onInserted.trigger(event.target, filter);
				};
			
				if(!$._onInserted.css) {
					$("head").append('<style id="onInserted-css" type="text/css"></style>');
					$._onInserted.css = $("#onInserted-css");
			
					if($.support.mutationObservers) {
						// DOM4 MutationObservers (https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
						$._onInserted.observer = new $.support.mutationObservers(mutations_snitch);
					} else if($.support.transition) {
						// CSS animation hack: (http://davidwalsh.name/detect-node-insertion)
						css = "@" + $._onInserted.css_proprietary() + "keyframes " + $._onInserted.CSS_ANIM_NAME + " { from { clip: rect(1px, auto, auto, auto); } to { clip: rect(0px, auto, auto, auto); } }";
						$._onInserted.css.append(css);
						if(document.addEventListener) {
							document.addEventListener("animationstart", css_snitch, false); // standard + firefox
							document.addEventListener("MSAnimationStart", css_snitch, false); // IE
							document.addEventListener("webkitAnimationStart", css_snitch, false); // Chrome + Safari
						}
					}
				}
				var css_selector = selector;
				var tokens = filter.split(",");
				for(var loop in tokens) {
					if(typeof(tokens[loop]) == "string") css_selector += ", " + selector + " " + tokens[loop].trim();
				}
				if($.support.mutationObservers) {
					// DOM4 MutationObservers (https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
					var config = { childList: true, subtree: true };
					this.each(function() { $._onInserted.observer.observe(this, config); });
				} else if($.support.transition) {
					// CSS animation hack: (http://davidwalsh.name/detect-node-insertion)
					var css_prop = $._onInserted.css_proprietary();
					css = css_selector + " { " + css_prop + "animation-duration: 0.001s; " + css_prop + "animation-name: " + $._onInserted.CSS_ANIM_NAME + "; }";
					$._onInserted.css.append(css);
				} else if($.support.mutationEvents) {
					// DOM3 Mutation events
					/*
					WARNING: no filter ("*") here can make the page become unresponsive, if rooted on document or body.
					It is therefore recommended to *always* provide a filter, even if it's "div, span, p, :input" or something as explicit
					*/
					if(jMUI.$.fn.jquery < "1.7") self.delegate(filter, 'DOMNodeInserted DOMSubtreeModified', dom_snitch);
					else self.on('DOMNodeInserted DOMSubtreeModified', filter, dom_snitch);
				//} else if($.support.behaviors) { -- will not work in IE8 - expressions no longer supported
					// ie hack
				//	css = css_selector + "{behavior:expression(function(element){jMUI.$._onInserted.ie_snitch(element, '" + filter + "');runtimeStyle.behavior='none';}(this))}";
				//	$._onInserted.css.append(css);
				}
			}
			if(jMUI.$.fn.jquery < "1.7") self.delegate(filter, $._onInserted.EVENT_NAME, callback);
			else self.on($._onInserted.EVENT_NAME, filter, callback);
			if(filter != "*") self.find(filter).each(function() { $._onInserted.trigger($(this), filter); });
			return self;
		};
	
		$.support = $.support || {};
		// http://www.abeautifulsite.net/blog/2012/12/feature-detection-for-css-transitions-via-jquery-support/
		$.support.transition = $.support.transition || (function(){
			var element = document.body || document.documentElement,
				thisStyle = element.style,
				support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined;
			return support;
		})();
		// https://gist.github.com/stucox/5231211
		$.support.mutationObservers = (function () {
			var prefixes = ['WebKit', 'Moz', 'O', 'Ms', ''];
			for(var i=0; i < prefixes.length; i++) {
				if(prefixes[i] + 'MutationObserver' in window) return window[prefixes[i] + 'MutationObserver'];
			}
			return false;
		})();
		// https://github.com/dperini/nwevents/blob/ac33e52c1ed1c1c3a1bb1612384ca5b2f7a9b3ef/src/nwmatcher.js#L92
		$.support.mutationEvents = (function() {
			var element = document.body || document.documentElement;
			if(!element.addEventListener) return false;
		
			var supported = false;
			var initial_id = element.id;
			var callback = function() {
				element.removeEventListener('DOMAttrModified', callback, false);
				element.id = initial_id;
			};
			element.addEventListener('DOMAttrModified', callback, false);
			element.id = 'nw';
			supported = (element.id != 'nw');
			element.id = initial_id;
			return supported;
		})();
		$.support.behaviors = (function() {
			var element = document.body || document.documentElement;
			if(element.addBehavior) return true;
			return false;
		})();
	})(jMUI.$);
};
jMUI.internal._load = jMUI.internal._load || function() {
	if(!jMUI.ready) jMUI.internal._wait(function() { jMUI.internal._load(); });
	else {
		jMUI.internal.init();
		for(var loop = 0; loop < jMUI.inits.length; loop++) {
			if(typeof(jMUI.inits[loop]) == "function") jMUI.inits[loop]();
		}
		jMUI.inits = {push: function(f) { f(); }};
	}
};
jMUI.internal._comparable_version_number = function(version_string) {
	var tokens = version_string.split(".");
	var number = 0;
	var multiplier = 1;
	for(var loop = (tokens.length - 1); loop >= 0; loop--) {
		number += (parseInt(tokens[loop]) || 0) * multiplier;
		multiplier *= 100;
	}
	return number;
};
if((typeof(jQuery) != 'undefined') && (jMUI.internal._comparable_version_number(jQuery.fn.jquery) >= jMUI.internal._comparable_version_number(jMUI._min_jquery))) {
	// *** here we might need to doc.write if not the right jquery version, to avoid any timing effect where jquery-dependent code tries to use jquery right before our noconflict happens
	jMUI.$ = jQuery; // .noConflict(true); ? if true, REMOVES all traces of jquery, and then other scripts like vwo can't find $/jQuery anymore!
	jMUI.internal._load();
} else {
	jMUI.internal._injectScript(jMUI._protocol + "//ajax.googleapis.com/ajax/libs/jquery/" + jMUI._default_jquery + "/jquery.min.js", function() { // load min version so that in testing, we make sure to only use the earliest allowed version
		jMUI.$ = jQuery.noConflict(true); // .noConflict(typeof(jQuery) != 'undefined'); ?
		jMUI.internal._load();
	});
}


/**
 * The util module provides generic utilities for manipulating the DOM, or other general functionalities.
 * 
 * @module util
 * @title util
 * @namespace jMUI
 */
 
/**
 * These utilities provide generic utilities for manipulating the DOM, or other general functionalities.
 *
 * @class util
 * @namespace jMUI
 * @static
 */
jMUI.util = jMUI.util || {};
/**
 * Position Top Left.
 *
 * @property ktop_left
 * @type String
 * @final
 * @default "tl"
 */
jMUI.util.top_left = jMUI.util.top_left || "tl";
/**
 * Position Top Center.
 *
 * @property ktop_center
 * @type String
 * @final
 * @default "tc"
 */
jMUI.util.top_center = jMUI.util.top_center || "tc";
/**
 * Position Top Right.
 *
 * @property ktop_right
 * @type String
 * @final
 * @default "tr"
 */
jMUI.util.top_right = jMUI.util.top_right || "tr";
/**
 * Position Bottom Left.
 *
 * @property kbottom_left
 * @type String
 * @final
 * @default "bl"
 */
jMUI.util.bottom_left = jMUI.util.bottom_left || "bl";
/**
 * Position Bottom Center.
 *
 * @property kbottom_center
 * @type String
 * @final
 * @default "bc"
 */
jMUI.util.bottom_center = jMUI.util.bottom_center || "bc";
/**
 * Position Bottom Right.
 *
 * @property kbottom_right
 * @type String
 * @final
 * @default "br"
 */
jMUI.util.bottom_right = jMUI.util.bottom_right || "br";

/**
 * Time an operation.
 * 
 * <p>The first time this is called for a unique ID, the clock starts ticking. Ont he second call with the same ID, the clock is stopped, the elapsed time is returned (in milliseconds) and the clock is deleted. So if you call the method a third time with the same ID, a new clock is started.</p>
 *
 * @method clock
 *
 * @param {string} pClockName, a unique ID
 *
 * @return {null or integer} total execution time, in milliseconds
 *
 * @static
 */
jMUI.util.clock = jMUI.util.clock || function(pClockName) {
	if(typeof(jMUI._clocks[pClockName]) == "undefined") {
		jMUI._clocks[pClockName] = (new Date()).getTime();
		return null;
	}
	var delta = (new Date()).getTime() - jMUI._clocks[pClockName];
	delete jMUI._clocks[pClockName];
	return delta||0;
};
/**
 * Find out if the currently viewed page is a landing page.
 * 
 * <p>A page is considered a landing page if it is not on the same domain as the referrer.</p>
 *
 * @method is_landing_page
 *
 * @param {array} pIgnoreDomains a list of domains to ignore; domains in this list will, for all intents and purposes, be considered the same domain as the current domain.
 *
 * @return {boolean} is the currently viewed page a landing page?
 *
 * @static
 */
jMUI.util.is_landing_page = jMUI.util.is_landing_page || function(pIgnoreDomains) {
	var vReferringDomain = document.referrer.match(/https?\:\/\/([^/]+)/i);
	if(!vReferringDomain) return true;
	vReferringDomain = vReferringDomain[1];
	for(var vLoop=0; vLoop < pIgnoreDomains.length; vLoop++) {
		if(vReferringDomain.indexOf(pIgnoreDomains[vLoop]) >= 0) return false;
	}
	return true;
};
/**
 * Get a particular parameter from a URL string.
 * 
 * @method url_parameter
 *
 * @param {string} pURL URL string to anlayze.
 * @param {string} pParameterName the name of the parameter to extract.
 * @param {string} pDefault default value, if the parameter value is not found.
 *
 * @return {string} the value of the URL parameter, or the default value if the not found.
 *
 * @static
 */
jMUI.util.url_parameter = jMUI.util.url_parameter || function(pURL, pParameterName, pDefault) {
	if(!pURL) return pDefault;
	
	var pURL = pURL.split('#')[0];
	
	var vTokens = pURL.split('?');
	if(vTokens.length <= 1) return pDefault;
	
	//pURL = decodeURI(vTokens[1]);
	
	return jMUI.util.string.token_value(pURL, pParameterName, "&", "=", pDefault);
};
/**
 * Test if you can set cookies.
 * 
 * @method cookies_writable
 *
 * @return {bool} whether or not it is possible to write cookies on the current domain.
 *
 * @static
 */
jMUI.util.cookies_writable = jMUI.util.cookies_writable || function() {
	jMUI.util.set_cookie_value("monitus_cookie_test", ".", "/", document.domain, "1");
	var value = jMUI.util.cookie_value("monitus_cookie_test", null);
	if(value) {
		jMUI.util.set_cookie_value("monitus_cookie_test", "x", "/", document.domain, "1");
		return true;
	}
	return false;
};
/**
 * Get the value of a cookie.
 * 
 * @method cookie_value
 *
 * @param {string} pName name of the cookie to get.
 * @param {string} pDefault default value, if the cookie value is not found.
 *
 * @return {string} the value of the cookie, or the default value if the cookie does not yet exists.
 *
 * @static
 */
jMUI.util.cookie_value = jMUI.util.cookie_value || function(pName, pDefault) { return jMUI.util.string.token_value(document.cookie, pName, ";", "=", pDefault); };
/**
 * Set the value of a cookie, or delete it completely.
 * 
 * @method set_cookie_value
 *
 * @param {string} pName name of the cookie to set.
 * @param {string|number|date} pHours how long should ht ecookie be vlaid for? &quot;-&quot; is forever; &quot;.&quot; is no expiry date; &quot;x&quot; deletes the cookie; a number is hte number of hours the cookie is good for
 * @param {string} pPath path to set the cookie on.
 * @param {string} pDomain domain to set the cookie on.
 * @param {string} pValue value of the cookie to set.
 *
 * @static
 */
jMUI.util.set_cookie_value = jMUI.util.set_cookie_value || function(pName, pHours, pPath, pDomain, pValue) {
	if(pHours == "-") pHours = 43800; // "forever"
	var vExpiry = "";
	if(pHours == "x") {
		vExpiry = " expires=" + (new Date()).toGMTString() + ";"; // immediately. This seems to work better than "Fri, 02-Jan-1970 00:00:00 GMT" under some weird circumstances...
	} else if(pHours != ".") {
		vExpiry = ((pHours && typeof(pHours.toGMTString) == "function") ? pHours : new Date((new Date()).getTime() + (pHours * 3600000)));
		vExpiry = " expires=" + vExpiry.toGMTString() + ";";
	}
	document.cookie = pName + "=" + pValue + ";" + vExpiry + " path=" + pPath + "; domain=" + pDomain;
};
/**
 * Wait for a javascript object instance to be created in the window.
 *
 * @method wait_object
 *
 * @param {string} pObjectName the name of the intance to wait for; basically, the name of the variable.
 * @param {function} pCallBack the code to run when the instance is created.
 * @param {array} pArguments an array of arguments passed to the callback function.
 *
 * @static
 */
jMUI.util.wait_object = jMUI.util.wait_object || function(pObjectName, pCallBack, pArguments) {
	var vObject = jMUI.internal._test_object(pObjectName);
	if(vObject) {
		pArguments = pArguments || [vObject];
		pArguments.unshift(vObject);
		pCallBack.apply(this, pArguments);
	} else {
		var vInfo = jMUI._objectQueue[pObjectName];
		if(!vInfo) {
			vInfo = {callbacks: [], args: [], tries: 0};
			jMUI._objectQueue[pObjectName] = vInfo;
		}
		vInfo.callbacks.push(pCallBack);
		vInfo.args.push(pArguments);
		jMUI.internal._wait_object(pObjectName);
	}
};

/**
 * These utilities provide generic math utilities.
 *
 * @class math
 * @namespace jMUI.util
 * @static
 */
jMUI.util.math = jMUI.util.math || {};
/**
 * Integer division of two numbers.
 * 
 * @method int_div
 *
 * @param {number} pNumerator
 * @param {number} pDenominator
 *
 * @return {number} result of dividing pNumerator by pDenimoinator (integer).
 *
 * @static
 */
jMUI.util.math.int_div = jMUI.util.math.int_div || function(pNumerator, pDenominator) {
	var vRemainder = pNumerator % pDenominator;
	return ((pNumerator - vRemainder) / pDenominator);
};

/**
 * These utilities provide generic utilities for manipulating the DOM.
 *
 * @class dom
 * @namespace jMUI.util
 * @static
 */
jMUI.util.dom = jMUI.util.dom || {};
/**
 * Rename an element, by adding a suffix, and its dependants, if applicable.
 * For instance, some form field might have labels associated with it; this function
 * will also adjust the "for" attribute of the label.
 *
 * @method rename_element
 *
 * @param {jquery object} pElement the element to rename.
 * @param {string} pSuffix the suffix to add to the object's name.
 * @param {boolean} pDeep also rename all the object's children?
 *
 * @static
 */
jMUI.util.dom.rename_element = jMUI.util.dom.rename_element || function(pElement, pSuffix, pDeep) {
	if(!pElement || !pSuffix) return;
	
	var vAttributes = ["name", "id", "for"];
	var vAttribute = null;
	for(var vLoop = 0; vLoop < vAttributes.length; vLoop++) {
		vAttribute = pElement.attr(vAttributes[vLoop]);
		if(vAttribute) pElement.attr(vAttributes[vLoop], vAttribute + pSuffix);
	}
	
	if(pDeep) pElement.children().each(function() { jMUI.util.dom.rename_element(jMUI.$(this), pSuffix, pDeep); });
};

/**
 * These utilities provide generic utilities for manipulating strings.
 *
 * @class string
 * @namespace jMUI.util
 * @static
 */
jMUI.util.string = jMUI.util.string || {};
/**
 * Get the value of a tokenized string.
 * 
 * @method token_value
 *
 * @param {string} pString tokenized string.
 * @param {string} pTokenName name of the token to found.
 * @param {string} pTokenSeparator character or string used to separate the tokens from each other.
 * @param {string} pValueSeparator character or string used to separate the token name from its value.
 * @param {mixed} pDefault default value, if the token value is not found.
 *
 * @return {mized} the value of the token, or the default value if the token does not exist in pString.
 *
 * @static
 */
jMUI.util.string.token_value = jMUI.util.string.token_value || function(pString, pTokenName, pTokenSeparator, pValueSeparator, pDefault) {
	if(!pString || (pString == "") || !pTokenName || (pTokenName == "") || !pTokenSeparator || (pTokenSeparator == "") || !pValueSeparator || (pValueSeparator == "")) return pDefault;
	
	pString = ('' + pString);
	
	pTokenName = ('' + pTokenName) + pValueSeparator;
	var vStart = pString.indexOf(pTokenName);
	var vIndex = pTokenName.indexOf(pValueSeparator) + pValueSeparator.length;
	if(vStart >= 0) {
		var vEnd = pString.indexOf(pTokenSeparator, vStart);
		if(vEnd < 0) vEnd = pString.length;
		pDefault = pString.substring((vStart + vIndex), vEnd);
	}
	try {
		pDefault = (typeof(pDefault) == "string") ? decodeURIComponent(pDefault) : pDefault;
	} catch(e) {}
	return pDefault;
};
/**
 * Easy way to standardize strings; this function will return a default value if the input string is null.
 *
 * @method non_null_string
 *
 * @param {string} pString the string to test - or null.
 * @param {string} pDefault the default value to return, if the input string is, indeed, null.
 *
 * @return {string} the input string if not null, or else the default value.
 *
 * @static
 */
jMUI.util.string.non_null_string = jMUI.util.string.non_null_string || function(pString, pDefault) {
	if(!pDefault) pDefault = "";
	return (pString ? pString : pDefault);
};
/**
 * Inflate current form values into a template string. 
 * You insert special tokens in the template string, and they will be replaced by the equivalent form field value.
 * 
 * <p>Tokens are delimited by the percentage character (&quot;%&quot;), as such:<br /><br />
 * <code>The name field value is: %name%</code><br /><br />
 * In this case, the token &quot;%name%&quot; will be replaced by the value in the form field whoseid is &quot;name&quot;
 * </p>
 *
 * @method form_inflate
 *
 * @param {string} pTemplate the string to inflate.
 *
 * @return {string} the template string, with all tokens inflated.
 *
 * @static
 */
jMUI.util.string.form_inflate = jMUI.util.string.form_inflate || function(pTemplate) {
	if(pTemplate) {
		do {
			var vTokens = ("" + pTemplate).match(/%([^%]+)%/);
			if(vTokens) {
				var vRE = new RegExp(vTokens[0], "g");
				var vID = vTokens[1];
				if(vID.match(/\=/)) vID = "[" + vID + "]";
				else vID = "#" + vID;
				var vValue = "";
				var vObject = jMUI.$(":input" + vID);
				if(vObject.length > 0) {
					if((vObject.length > 1) && (vObject.first().attr("type").match(/radio/i))) vValue = jMUI.$(":input" + vID + ":checked").val();
					else vValue = vObject.val();
				}
				pTemplate = pTemplate.replace(vRE, vValue);
			}
		} while(pTemplate && vTokens);
	}
	return pTemplate;
};

/**
 * These utilities provide ui elments.
 *
 * @class ui
 * @namespace jMUI.util
 * @static
 */
jMUI.util.ui = jMUI.util.ui || {};
/**
 * Create a pop-up div; these pop-ups are not blocked by pop-up blockers, as they are not in a separate window.
 * 
 * @method create_pop_up
 *
 * @param {string} pText title of the button to show the pop-up.
 * @param {url} pImage url of the image used on the button. Optional.
 * @param {string} pPlaceholderSelector jquery selector of the element into which the button shoudl be placed.
 * @param {string} pPopUpID ID of the div to show as the pop-up content. If the div does not exist, one will be created.
 * @param {function} pPopUpFc function to be called when the button is clicked.
 *
 * @return {button} a reference to the created button; the button will have a &quot;popup&quot; property, for quick access to the popup div.
 *
 * @static
 */
jMUI.util.ui.create_pop_up = jMUI.util.ui.create_pop_up || function(pText, pImage, pPlaceholderSelector, pPopUpID, pPopUpFc) {
	var vButton = null;
	try {
		var vPlaceholder = jMUI.$(pPlaceholderSelector);
		if(vPlaceholder.length > 0) {
			var vPopUp = jMUI.$("#" + pPopUpID);
			if(vPopUp.length == 0) {
				jMUI.$("body").append("<div id='" + pPopUpID + "'></div>");
				vPopUp = jMUI.$("#" + pPopUpID);
			}
			vPopUp.hide();
			
			var vButton = vPlaceholder.append("<a href='#' title='" + pTitle + "'></a>").css("border", "none").click(pPopUpFc);
			
			if(pImage) vButton.append("<img src='" + pImage + "' />").css("border", "none");
			else vButton.append(pText);
			vButton.data("popup", vPopUp);
		}
	} catch(e) { }
	return vButton;
};
/**
 * Badge an image: adds an image (badge) over another image (target).
 * 
 * @method badge_image
 *
 * @param {url} pBadgeURL url of the badge image; the image should be a file with a transparent background.
 * @param {string|jquery object} pSelectorOrObject the jquery selector to find the target image to badge OR the target image directly, as a jquery object.
 * @param {string} pPosition one of: jMUI.util.top_right (default), jMUI.util.top_center, jMUI.util.top_left, jMUI.util.bottom_right, jMUI.util.bottom_center or jMUI.util.bottom_left
 *
 * @static
 */
jMUI.util.ui.badge_image = jMUI.util.ui.badge_image || function(pBadgeURL, pSelectorOrObject, pPosition, pTargetSize, pBadgeSize, waiting) {
	if(!pPosition) pPosition = "tr";
	pBadgeURL = jMUI._protocol + pBadgeURL.replace(/^https?\:/i, "");
	
	var vTarget = (typeof(pSelectorOrObject) == "string") ? jMUI.$(pSelectorOrObject) : pSelectorOrObject;
	if((typeof(pSelectorOrObject) == "string") && !vTarget) {
		if(waiting) return true;
		jMUI.internal._wait(function(w) { jMUI.util.ui.badge_image(pBadgeURL, pSelectorOrObject, pPosition, null, null, w); });
	} else if(vTarget.next("img.__mui_badge").length == 0) {
		var vWidthProp = "naturalWidth";
		if(typeof(vTarget[vWidthProp]) == "undefined") vWidthProp = "width";
		var vHeightProp = "naturalHeight";
		if(typeof(vTarget[vHeightProp]) == "undefined") vHeightProp = "height";
		
		var vBadgeURL = pBadgeURL;
		var vSelector = pSelectorOrObject;
		var vPosition = pPosition;
		
		if(!pTargetSize) {
			var vImage = new Image();
			vImage.onload = function() {
				jMUI.util.ui.badge_image(vBadgeURL, vSelector, vPosition, [vImage[vWidthProp], vImage[vHeightProp]]);
			};
			var targetURL = vTarget.attr("src");
			targetURL += (targetURL.match(/\?/i) ? "&" : "?") + "_x=" + (new Date()).getTime(); // thwart cache...
			vImage.src = targetURL;
		}else if(!pBadgeSize) {
			var vTargetSize = pTargetSize;
			var vImage = new Image();
			vImage.onload = function() {
				jMUI.util.ui.badge_image(vBadgeURL, vSelector, vPosition, vTargetSize, [vImage[vWidthProp], vImage[vHeightProp]]);
			};
			vImage.src = pBadgeURL + (pBadgeURL.match(/\?/i) ? "&" : "?") + "_x=" + (new Date()).getTime(); // thwart cache...
		} else if((pTargetSize.length >= 2) && (pBadgeSize.length >= 2)) {
			var vAlign = vTarget.attr("align");
			var inline = (vTarget.parent().css("display") == "inline");
			
			var vWrapper = vTarget.before("<span></span>").prev().css("position", "relative").append(vTarget);
			vTarget.css("position", "relative");
			
			var vBadge = vWrapper.append("<img class='__mui_badge' src='" + pBadgeURL + "' />").children(":last").css({
				"position": "absolute",
				"border": "none",
				"zIndex" : 1000000,
				"width": pBadgeSize[0] + "px",
				"height": pBadgeSize[1] + "px"
			});
			
			if(inline) {
				vWrapper.css("display", "inline-block");
				vWrapper.parent().css("display", "inline-block");
			}
			
			if(vAlign && (vAlign.match(/left/i) || vAlign.match(/right/i))) vBadge.css({"float": vAlign}).attr("align", "");
			
			if(pPosition.match(/^b/i)) vBadge.css("bottom", (navigator.userAgent.match(/msie/i) ? "0" : "2") + "px");
			else vBadge.css("top", "auto");
			if(navigator.userAgent.match(/msie/i) && pPosition.match(/r$/i)) vBadge.css("left", (pTargetSize[0] - pBadgeSize[0]) + "px");
			else if(pPosition.match(/r$/i)) vBadge.css("right", "0px");
			else if(pPosition.match(/c$/i)) vBadge.css("left", jMUI.util.math.int_div((pTargetSize[0] - pBadgeSize[0]), 2) + "px");
			else vBadge.css("left", "0px");
		}
	}
	return false;
};
/**
 * Badge a collection of images: adds an image (badge) over another image (target).
 * 
 * @method badge_images
 *
 * @param {url} pBadgeURL url of the badge image; the image should be a file with a transparent background.
 * @param {string} pPredicate a jquery selector or an array of jquery selectors.
 * @param {string} pPosition one of: jMUI.util.top_right (default), jMUI.util.top_center, jMUI.util.top_left, jMUI.util.bottom_right, jMUI.util.bottom_center or jMUI.util.bottom_left
 *
 * @static
 */
jMUI.util.ui.badge_images = jMUI.util.ui.badge_images || function(pBadgeURL, pPredicate, pPosition, pNoWait) {
	// pPosition is "tl", "tr", "bl" or "br" -- default is "tr"
	var vHotCache = new Image();
	vHotCache.src = pBadgeURL;
	if(typeof(pPredicate) == "string") {
		if(pNoWait) {
			var vObjects = jMUI.$(pPredicate);
			if(vObjects.length == 1) jMUI.util.ui.badge_image(pBadgeURL, pPredicate, pPosition);
			else {
				for(var vLoop = 0; vLoop < vObjects.length; vLoop++) jMUI.util.ui.badge_image(pBadgeURL, vObjects[vLoop], pPosition);
			}
		} else {
			var vBadgeURL = pBadgeURL;
			var vPredicate = pPredicate;
			var vPosition = pPosition;
			jMUI.$(function() {
				jMUI.util.ui.badge_images(vBadgeURL, vPredicate, vPosition, true);
			});
		}
	} else {
		for(var vLoop = 0; vLoop < pPredicate.length; vLoop++) jMUI.util.ui.badge_image(pBadgeURL, pPredicate[vLoop], pPosition);
	}
};
jMUI.util.ui.widgets = jMUI.util.ui.widgets || {};
/**
 * Show a countdown timer.
 * 
 * @method countdown
 *
 * @param {string} pCountdownSelector a jquery selector to the dom element where the counter should be drawn; inside it, there should be a span with the class "countdown"
 * @param {int} pStart number of seconds left.
 * @param {string} pCountdownOverSelector a jquery selector to the dom element shown once countdown is over. Optional. Default: null
 *
 * @static
 */
jMUI.util.ui.widgets.countdown = jMUI.util.ui.widgets.countdown || function(pCountdownSelector, pStart, pCountdownOverSelector, waiting) {
	var countdown_container = jMUI.$(pCountdownSelector);
	if(!countdown_container) {
		if(waiting) return true;
		jMUI.internal._wait(function(w) { jMUI.util.ui.widgets.countdown(pCountdownSelector, pStart, pCountdownOverSelector, w); });
	} else {
		countdown_container.hide();
		var countdown_over = jMUI.$(pCountdownOverSelector).hide();
		var timer = countdown_container.data("timer");
		if(timer) clearInterval(timer);
		var seconds = pStart;
		var countdown = function() {
			var over = (seconds <= 0);
			if(over) {
				html = "00:00:00";
			} else {
				var sec = seconds--;
				var hours = Math.floor(sec / 3600);
				var minutes = Math.floor((sec - (hours * 3600)) / 60);
				var sec = sec - (hours * 3600) - (minutes * 60);
				
				if (hours < 10) hours = "0" + hours;
				if (minutes < 10) minutes = "0" + minutes;
				if (sec < 10) sec = "0" + sec;
				html = hours + ':' + minutes + ':' + sec;
			}
			countdown_container.find(".countdown").html(html);
			countdown_container.toggle(!over);
			countdown_over.toggle(over);
		};
		countdown();
		countdown_container.data("timer", setInterval(countdown, 1000));
	}
	return false;
};

jMUI._test_mode = jMUI.util.cookie_value("_mpm", false); // "Monitus Puppet Master"
jMUI.ready = true;

Copyright © 2017 Yahoo! Inc. All rights reserved.