﻿/******************************************************************************
**	Desc: The Utilities class. Contains the commonly used utility methods.
**	Auth: Aleksey Fomichenko
******************************************************************************/

Type.registerNamespace("Alesha911");

var $util = Alesha911.Utilities =
{
	getRandomNumber: function(low, high)
	{
		/// <summary>Returns a random number between the low and high boundaries.</summary>
		/// <param name="low" type="Number" optional="false" mayBeNull="false">The lowest possible number.</param>
		/// <param name="high" type="Number" optional="false" mayBeNull="false">The highest possible number.</param>
		/// <returns type="Number" mayBeNull="false"></returns>

		return Math.floor((Math.random() * (high - low + 1)) + low);
	},

	htmlEncode: function(str)
	{
		/// <summary>Html-encodes the specified string.</summary>
		/// <param name="str" type="String" optional="false" mayBeNull="false">The string to html-encode.</param>
		/// <returns type="String" mayBeNull="false"></returns>

		return str.replace(/&/gi, "&amp;").replace(/</gi, "&lt;").replace(/>/gi, "&gt;");
	},

	htmlDecode: function(str)
	{
		/// <summary>Decodes the html-encoded string.</summary>
		/// <param name="str" type="String" optional="false" mayBeNull="false">The string to decode.</param>
		/// <returns type="String" mayBeNull="false"></returns>

		return str.replace(/&gt;/gi, ">").replace(/&lt;/gi, "<").replace(/&amp;/gi, "&");
	},
	
	removeCurrency: function(value)
	{
		/// <summary>Removes currency formatting from source string.</summary>
		/// <param name="value" type="String" optional="false" mayBeNull="false">Source string from which currency formatting will be removed.</param>
		/// <returns type="String" mayBeNull="false">Source string with commas removed.</returns>

		var objRegExp = /\(/;
		var strMinus = String.empty;

		//check if negative
		if (objRegExp.test(value))
			strMinus = '-';

		objRegExp = /\)|\(|[,]/g;
		value = value.replace(objRegExp, '');
		if (value.indexOf('$') >= 0)
			value = value.substring(1, value.length);

		return strMinus + value;
	},

	addCurrency : function (value)
	{
		/// <summary>Formats a number as currency. Assumes number passed is a valid numeric value in the rounded to 2 decimal places. If not, returns original value.</summary>
		/// <param name="value" type="String">Source string to be formatted.</param>

		var objRegExp = new RegExp("-?[0-9]+(\\.[0-9]{2})?");
		var s = value.toString();
		if (objRegExp.test(s))
		{
			s = $util.addCommas(s);
			if (s.startsWith("-"))
				s = "(${0})".format(s.replace("-", String.empty));
			else
				s = "${0}".format(s);
		}
		return s;
	},

	removeCommas : function (value)
	{
		/// <summary>Removes commas from source string.</summary>
		/// <param name="value" type="String" optional="false" mayBeNull="false">Source string from which commas will be removed.</param>
		/// <returns type="String" mayBeNull="false"></returns>

		var objRegExp = /,/g; //search for commas globally
		return value.replace(objRegExp, String.empty);
	},

	addCommas : function (value)
	{
		/// <summary>Inserts commas into numeric string. Used with integers or numbers with 2 or less decimal places.</summary>
		/// <param name="value" type="String" optional="false" mayBeNull="false">Source string to add commas to.</param>
		/// <returns type="String" mayBeNull="false">String modified with comma grouping if source was all numeric, otherwise source is returned.</returns>

		var str = value.toString();
		var decimalIdx = str.indexOf(".");
		var decimal = "";

		if (decimalIdx > -1)
		{
			decimal = str.substr(decimalIdx);
			str = str.substr(0, decimalIdx);
		}

		var objRegExp = /(-?[0-9]+)([0-9]{3})/;
		while (objRegExp.test(str))
		{
			//replace original string with first group match, a comma, then second group match
			str = str.replace(objRegExp, '$1,$2');
		}

		return str + decimal;
	},

	removeWhiteSpace : function (value)
	{
		/// <summary>Removes whitespace character from the source string.</summary>
		/// <param name="value" type="String" optional="false" mayBeNull="false">Source string to remove the whitespace character from.</param>
		/// <returns type="String" mayBeNull="false"></returns>

		return value.replace(/\s/g, '');
	},

	addEllipses : function (value, maxLength, ellipseToUse)
	{
		/// <summary></summary>
		/// <param name="value" type="String" optional="false" mayBeNull="false"></param>
		/// <param name="maxLength" type="String" optional="false" mayBeNull="false"></param>
		/// <param name="ellipseToUse" type="String" optional="false" mayBeNull="false"></param>
		/// <returns type="String" mayBeNull="false"></returns>

		if (!value) return String.empty;

		if (typeof(ellipseToUse) === $const.undefined || !ellipseToUse) ellipseToUse = "&#0133;";
		value = value.trim();
		return (value.length > maxLength) ?
			value.substring(0, maxLength - ellipseToUse.length).trim() + ellipseToUse :
			value;
	},

	getEvent : function (evt)
	{
		/// <summary>Gets the current event.</summary>

		return (evt ? evt : window.event);
	},

	getEventKey : function (evt)
	{
		/// <summary>Gets the keyboard key code associated with the event</summary>

		evt = this.getEvent(evt);
		if (evt) return (evt.charCode) ? evt.charCode : ((evt.keyCode) ? evt.keyCode : ((evt.which) ? evt.which : 0));
	},

	freezeEvent : function (evt)
	{
		/// <summary>Freezes the event from bubbling and doing any default actions.</summary>

		if (evt.preventDefault) evt.preventDefault();
		evt.returnValue = false;
		evt.cancelBubble = true;
		if (evt.stopPropagation) evt.stopPropagation();
		return false;
	},

	isSubmitKey : function (evt, treatSpaceAsSubmit)
	{
		/// <summary>Returns a boolean value indicating whether the key that was pressed is a submit key.</summary>
		/// <param name="evt" type="Object" optional="false" mayBeNull="false">The event associated with the key press.</param>
		/// <param name="treatSpaceAsSubmit" type="Boolean" optional="true" mayBeNull="false">Whether to treat the space key as a submit key.</param>

		var key = this.getEventKey(evt);
		if (key == Sys.UI.Key.enter || (treatSpaceAsSubmit && key == Sys.UI.Key.space))
		{
			this.freezeEvent(evt);
			return true;
		}
		return false;
	},

	formatNumericTextbox : function (source, evt)
	{
		/// <summary>Formats a numeric value in a textbox.</summary>
		/// <param name="source" type="DOMElement" optional="false" mayBeNull="false">The textbox whose value to format.</param>

		var key = this.getEventKey(evt);
		//trace(key);
		switch (key)
		{
			case 16: // shift
			case 190: // dot (.)
			case 110: // dot (.) on the num-pad
			case Sys.UI.Key.tab: return;
			case 48: // zero (0)
			{
				if (source.value.endsWith("0") && source.value.indexOf(".") > -1) return;
			}
			default: break;
		}

		var num = Number.ensureNumber(source.value, null);

		if (!num) return;

		source.value = this.addCommas(num);
	},

	formatCurrencyTextbox : function (source, evt)
	{
		/// <summary>Formats a currency value in a textbox.</summary>
		/// <param name="source" type="DOMElement">The textbox whose value to format.</param>

		var key = this.getEventKey(evt);
		switch (key)
		{
			case 16: // Shift Key
			case Sys.UI.Key.tab: break;
			default: source.value = this.addCurrency(this.removeCurrency(source.value)); break;
		}
	},

	setCookie : function (name, value, expires, path, domain, secure)
	{
		/// <summary>Sets the client browser cookie.</summary>
		/// <param name="name" type="String" optional="false" mayBeNull="false">The name of the cookie to set.</param>
		/// <param name="value" type="String" optional="false" mayBeNull="false">The value of the cookie to set.</param>
		/// <param name="expires" type="Date" optional="true" mayBeNull="false">The expiration date of the cookie (defaults to end of current session).</param>
		/// <param name="path" type="String" optional="true" mayBeNull="false">The path for which the cookie is valid (defaults to path of calling document).</param>
		/// <param name="domain" type="String" optional="true" mayBeNull="false">The domain for which the cookie is valid (defaults to domain of calling document).</param>
		/// <param name="secure" type="Boolean" optional="true" mayBeNull="false">Indicates wheter the cookie transmission requires a secure transmission.</param>

		var cookieStr = name + "=" + escape(value) +
			((expires) ? "; expires=" + expires.toGMTString() : "") +
			((path) ? "; path=" + path : "; path=/") +
			((domain) ? "; domain=" + domain : "") +
			((secure) ? "; secure" : "");

		document.cookie = cookieStr;
	},

	getCookie : function (name)
	{
		/// <summary>Gets the client browser cookie by name.</summary>

		// first we'll split this cookie up into name/value pairs
		// note: document.cookie only returns name=value, not the other components
		var a_all_cookies = document.cookie.split(';');
		var a_temp_cookie = '';
		var cookie_name = '';
		var cookie_value = '';
		var b_cookie_found = false; // set boolean t/f default f

		for (i = 0; i < a_all_cookies.length; i++)
		{
			// now we'll split apart each name=value pair
			a_temp_cookie = a_all_cookies[i].split('=');
			
			// and trim left/right whitespace while we're at it
			cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');

			// if the extracted name matches passed name
			if (cookie_name == name)
			{
				b_cookie_found = true;

				// we need to handle case where cookie has no value but exists (no = sign, that is):
				if (a_temp_cookie.length > 1)
					cookie_value = unescape(a_temp_cookie[1].replace(/^\s+|\s+$/g, ''));
				
				// note that in cases where cookie is initialized but no value, null is returned
				return cookie_value;
			}
			a_temp_cookie = null;
			cookie_name = '';
		}

		return null;
	},

	openNewWindow: function(url, name, width, height, top, left, displayStatusBar, displayScrollbars, allowResizing, displayMenubar, displayToolbar, replace)
	{
		/// <summary>Opens a new window and loads the document specified by a given URL.</summary>
		/// <param name="url" type="String" optional="false" mayBeNull="false">Required. A String that specifies the URL to render in the new window.</param>
		/// <param name="name" type="String" optional="true" mayBeNull="true">Optional. A String that specifies the name of the window.</param>
		/// <param name="width" type="Number" optional="true" mayBeNull="true">Optional. Sets the width of the window in pixels. The minimum value is 250, and specifies the minimum width of the browser content area.</param>
		/// <param name="height" type="Number" optional="true" mayBeNull="true">Optional. Sets the height of the window in pixels. The minimum value is 150, and specifies the minimum height of the browser content area.</param>
		/// <param name="top" type="Number" optional="true" mayBeNull="true">Optional. Specifies the top position, in pixels. This value is relative to the upper-left corner of the screen. The value must be greater than or equal to zero.</param>
		/// <param name="left" type="Number" optional="true" mayBeNull="true">Optional. Specifies the left position, in pixels. This value is relative to the upper-left corner of the screen. The value must be greater than or equal to zero.</param>
		/// <param name="displayStatusBar" type="Boolean" optional="true" mayBeNull="true">Optional. Specifies whether to add a Status Bar at the bottom of the window.</param>
		/// <param name="displayScrollbars" type="Boolean" optional="true" mayBeNull="true">Optional. Specifies whether to display horizontal and vertical scroll bars.</param>
		/// <param name="allowResizing" type="Boolean" optional="true" mayBeNull="true">Optional. Specifies whether to display resize handles at the corners of the window.</param>
		/// <param name="displayMenubar" type="Boolean" optional="true" mayBeNull="true">Optional. Specifies whether to display the menu bar.</param>
		/// <param name="displayToolbar" type="Boolean" optional="true" mayBeNull="true">Optional. Specifies whether to display the browser toolbar.</param>
		/// <param name="replace" type="Boolean" optional="true" mayBeNull="true">Optional. When the url is loaded into the same window, this Boolean parameter specifies whether the url creates a new entry or replaces the current entry in the window's history list.</param>

		var features = "{0}{1}{2}{3}{4}{5}{6}".format(
			Number.isNumber(width) ? "width={0},".format(width) : String.empty,
			Number.isNumber(height) ? "height={0},".format(height) : String.empty,
			Number.isNumber(top) ? "top={0},".format(top) : String.empty,
			Number.isNumber(left) ? "left={0},".format(left) : String.empty,
			displayStatusBar === true ? "status=yes," : "status=no,",
			displayScrollbars === true ? "scrollbars=yes," : "scrollbars=no,",
			allowResizing === true ? "resizable=yes," : "resizable=no,",
			displayMenubar === true ? "menubar=yes," : "menubar=no,",
			displayToolbar === true ? "toolbar=yes," : "toolbar=no,");

		if (features.length > 0) features = features.substring(0, features.length - 1);

		trace("New window features: {0}".format(features));

		newWin = window.open(url, name, features, replace);
		if (newWin) newWin.focus();
	},

	getCurrentTime: function()
	{
		/// <summary>Gets the current time in milliseconds.</summary>

		return new Date().getTime();
	},

	getElapsedTime: function(startTime)
	{
		/// <summary>Gets the number of milliseconds from the start time to the current time.</summary>
		/// <param name="startTime" type="Number" optional="false" mayBeNull="false">The start time in milliseconds.</param>
		/// <returns type="Number" mayBeNull="false"></returns>

		return this.getCurrentTime() - (startTime * 1);
	},
		
	dateAdd : function (sourceDate, numberOfDaysToAdd)
	{
		if (!sourceDate) sourceDate = new Date();
		return new Date(sourceDate.setDate(sourceDate.getDate() + numberOfDaysToAdd));
	},
	
	countWords: function(value)
	{
		/// <summary>Returns the number of words in a specified string.</summary>
		/// <param name="value" type="String"></param>
		/// <returns type="Number" mayBeNull="false"></returns>

		var fullStr = value + " ";
		var initial_whitespace_rExp = /^[^A-Za-z0-9]+/gi;
		var left_trimmedStr = fullStr.replace(initial_whitespace_rExp, "");
		var non_alphanumerics_rExp = rExp = /[^A-Za-z0-9]+/gi;
		var cleanedStr = left_trimmedStr.replace(non_alphanumerics_rExp, " ");
		var splitString = cleanedStr.split(" ");
		var word_count = splitString.length - 1;

		if (fullStr.length < 2)
		{
			word_count = 0;
		}

		return word_count;
	},

	getQueryStringParameter : function(keyValuePair, treatAsFirstParameter)
	{
		/// <summary>Generates the key/value pair for the QueryString, including the & or the ? marks.</summary>
		/// <param name="keyValuePair" type="Rpr.KeyValuePair">The Rpr.KeyValuePair instance to get the key and a value from.</param>
		/// <param name="treatAsFirstParameter" type="Boolean" optional="true" mayBeNull="true">If set to true, the '?' mark will be used instead of the '&' mark.</param>

		if (String.isNullOrEmpty(keyValuePair.value))
			return String.empty;

		return "{0}{1}={2}".format(treatAsFirstParameter ? "?" : "&", keyValuePair.key, escape(keyValuePair.value));
	},

	generateUrl : function(baseUrl, keyValuePairs)
	{
		/// <summary>Generates the URL from the base url and the collections of keys and values.</summary>
		/// <param name="baseUrl" type="String">The url to append the params to.</param>
		/// <param name="keyValuePairs" type="Array">The collection of Rpr.KeyValuePair objects.</param>
		
		var firstParamFound = String.parse(baseUrl).indexOf("?") > 0;

		if (keyValuePairs)
		{
			for (var i = 0; i < keyValuePairs.length; i++)
			{
				var param = $util.getQueryStringParameter(keyValuePairs[i], !firstParamFound);

				if (!firstParamFound && !String.isNullOrEmpty(param))
					firstParamFound = true;

				baseUrl += param;
			}
		}
		return baseUrl;
	},
	
	redirect : function (url, delay)
	{
		/// <summary>Redirects the user to a new URL.</summary>
		/// <param name="url" type="String">The url to redirect to.</param>
		/// <param name="delay" type="Number" optional="true" mayBeNull="false">Optional: the number of milliseconds to delay the redirection via setTimeout.</param>
		
		if (!delay || !Number.isNumber(delay))
		{
			location.href = url;
			return;
		}
		
		window.setTimeout(function () { location.href = url; }, delay);
	},
	
	getControlValue : function (jQueryObject)
	{
		/// <summary>Gets the value of a control/element.</summary>
		/// <param name="jQueryObject" type="jQuery">Required: the jQuery object to get the value from.</param>
		
		var value = null;
		
		if (jQueryObject.is(":checkbox"))
			value = jQueryObject.is(":checked");
		else if (jQueryObject.attr("ufdValue") !== undefined)
			value = jQueryObject.attr("ufdValue");
		else if (jQueryObject.is(":text") || jQueryObject.is(":password") || jQueryObject.is("input[type='hidden']") || jQueryObject.is("textarea") || jQueryObject.is("select"))
			value = jQueryObject.val();
		else if (jQueryObject.is("div") && (jQueryObject.is("[value]") || jQueryObject.is("[value='']")))
			value = jQueryObject.attr("value");
		else
			traceWarning("This type of element is not setup to automatically retrieve its value. PropertyName: '{0}', CssClass: '{1}'".format(jQueryObject.attr("PropertyName"), jQueryObject.attr("class")));

		return value;
	},

	isEmptyObject : function (o)
	{
		/// <summary>Checks whether the specified object has at least one non-null basic property.</summary>

		trace("isEmptyObject begin...", true);

		var valueFound = false;
		
		$.each(o, function (key, value)
		{
			trace("{0}: {1} ({2})".format(key, value, typeof(value)));

			if (value === null) return true;

			var vt = typeof(value);

			switch (vt)
			{
				case "function" :
				case "boolean" : return true;
				case "string" : if (!String.isNullOrEmpty(value)) { valueFound = true; return false; }
				case "number" : if (value !== 0) { valueFound = true; return false; }
				case "object" :
				{
					// check if the value is an array...
					if (typeof(value.length) === "number")
					{
						//TODO: [AF] - Enhance this logic to check each element of the array, not just the presense of items in the array...
						if (value.length > 0)
						{
							valueFound = true;
							return false;
						}
					}
					else
					{
						if (!$util.isEmptyObject(value))
						{
							valueFound = true;
							return false;
						}
					}
					return true;
				}
				default : return true;
			}
		});

		trace("RESULT: {0}".format(!valueFound));
		trace(null, false);

		return !valueFound;
	},

	addOptionToDropDownList : function (ddl, text, value)
	{
		/// <summary>Adds a new option to the 'select' html control.</summary>

		var o = document.createElement("OPTION");
		ddl.options.add(o);
		if (typeof(text) === $const.undefined) { text = value; }
		if (typeof(value) === $const.undefined) { value = text; }
		o.innerHTML = String.parse(text);
		o.value = String.parse(value);
	},
	
	bindDropDownList : function (element, dataSource, dataValueField, dataTextField)
	{
		/// <summary>Adds the options to the 'select' control.</summary>
		/// <param name="element" type="Sys.UI.DomElement" optional="false" mayBeNull="false">The DropDownList (select) control.</param>
		/// <param name="dataSource" type="Array" optional="false" mayBeNull="false">The array of data items. The data item must contain a 'text' and a 'value' properties.</param>
		/// <param name="dataTextField" type="String" optional="true" mayBeNull="false">The name of the field that contains the 'text' of the item. Default is 'text'.</param>
		/// <param name="dataValueField" type="String" optional="true" mayBeNull="false">The name of the field that contains the 'value' of the item. Default is 'value'.</param>
		
		$(element).empty();

		if (!dataSource || dataSource.length < 1)
			return;

		if (typeof(dataSource[0]) === "string")
		{
			for (var i = 0; i < dataSource.length; i++)
				$util.addOptionToDropDownList(element, dataSource[i]);

			return;
		}

		if (!dataTextField) dataTextField = "text";
		if (!dataValueField) dataValueField = "value";
		
		for (var j = 0; j < dataSource.length; j++)
			$util.addOptionToDropDownList(element, dataSource[j][dataTextField], dataSource[j][dataValueField]);
	},

	scrollToElementIfNotInViewport : function (selector, callback)
	{
		//TODO: [AF] - Come back and clean-up...
		var el = $(selector);

		if (el.is(":in-viewport"))
		{
			if (typeof(callback) === "function") callback.apply(this);
			return;
		}

		$('html, body').animate({scrollTop : el.offset().top}, 'normal', null, function () { if (typeof(callback) === "function") callback.apply(this); });
	}
};
