﻿/***********************************************************
 *		stingray engineering library
 ***********************************************************/

/**
 * stingray engineering namespaces
 * data - objects for retrieving, manipulating and encapsulating data
 * dhtml - objects for DHTML wizardry and widgetry
 * forms - objects which back form elements
 * util - the ever popular "util" namespace, all the cool kids have one
 * validation - form validation functions
 */
var stingray = function() {
    return { 
		data: {}, 
		dhtml: {}, 
		forms: {}, 
		util: {}, 
		validation: {},
        /**
         * Returns the namespace specified and creates it if it doesn't exist
         * @param  {String} sNameSpace String representation of the desired name
         * @return {Object} the namespace object
         */
        getNamespace: function( strNS ) {
            if (strNS != null && strNS.length>0) {
				// split it up
				var levels = strNS.split(".");
				// skip implied "stingray" namespace
				var currentNS = stingray;
				var start = (levels[0]=='stingray') ? 1 : 0;
				// get or create each sub-space
				for (var i=start; i<levels.length; ++i) {
					currentNS[levels[i]] = currentNS[levels[i]] || {};
					currentNS = currentNS[levels[i]];
				}
				return currentNS;
            } else {
                return null;
            }
        }
	};
}();


/*****************************    DATA NAMESPACE    *****************************/


/*****************************    DHTML NAMESPACE    *****************************/


/*****************************    UTIL NAMESPACE    *****************************/


/**
 * the ever so handy browser sniffer
 * @class stingray.util.browser
 * @author brian@stingrayengineering.com
 * @constructor
 */
stingray.util.browser = function() {
	// box model, use "isW3CBox" function
	this.w3CBox = null;
	// raw copies of navigator props
	this.uaRaw = navigator.userAgent;
	this.nameRaw = navigator.appName;
	// browser name
	var b=navigator.appName;
	if (b.indexOf('Netscape')!=-1) this.b="ns";
	else if ((b=="Opera") || (navigator.userAgent.indexOf("Opera")>0)) this.b = "opera";
	else if (b=="Microsoft Internet Explorer") this.b="ie";
	// versions and convenience booleans
	this.version=navigator.appVersion;
	this.v=parseInt(this.version);
	this.ns=(this.b=="ns" && this.v>=4);
	this.ns4=(this.b=="ns" && this.v==4);
	this.ns6=(this.b=="ns" && this.v==5);
	this.ie=(this.b=="ie" && this.v>=4);
	this.ie4=(this.version.indexOf('MSIE 4')>0);
	this.ie5=(this.version.indexOf('MSIE 5')>0);
	this.ie55=(this.version.indexOf('MSIE 5.5')>0);
	this.ie6=(this.version.indexOf('MSIE 6.0')>0);
	this.opera=(this.b=="opera");
	this.firefox = (this.b=="ns" && (navigator.userAgent.indexOf("Firefox")>0) );
	this.dom=(document.createElement && document.appendChild && document.getElementsByTagName)?true:false;
	// OS/platform
	var ua=navigator.userAgent.toLowerCase();
	if (ua.indexOf("win")>-1) this.platform="win32";
	else if (ua.indexOf("mac")>-1) this.platform="mac";
	else this.platform="other";
}
/**
 * indicates whether the browser supports the W3C box model
 * @return {boolean} 
 */
stingray.util.browser.prototype.isW3CBox = function() {
	if ( this.w3CBox == null ) {
		var tb = document.createElement("DIV");
		tb.style.width='10px';
		tb.style.padding = '1px';
		document.getElementsByTagName("BODY")[0].appendChild(tb);
		this.w3CBox = (tb.offsetWidth > 10) ? true : false;
		tb.parentNode.removeChild(tb);
	}
	return this.w3CBox;
}


/**
 * general utilities object
 * @class stingray.util.utilities
 * @author brian@stingrayengineering.com
 * @constructor
 */
stingray.util.utilities = function() {}

/**
 * open a popup
 * @param _url {String} url of the window
 * @param _name {String} name of the window
 * @param _features {String} features of the window (ie: left=20,top=20,width=800,height=500)
 * @param _focus {boolean} focus the window after opening
 */
stingray.util.utilities.prototype.openWin = function( _url, _name, _features, _focus ) {
	var w = window.open(_url, _name, _features);
	if ( _focus == true ) {
		w.focus();
	}
}


/**
 * url parsing class, deals with HTTP params
 * @class stingray.util.urlParser
 * @author brian@stingrayengineering.com
 * @constructor
 */
stingray.util.urlParser = function(_url) {
	if ( arguments.length > 0 ) {
		this.init(_url);
	}
}
/**
 * intitializes the url parser
 */
stingray.util.urlParser.prototype.init = function(_url) {
	this.rawURL = _url;
	this.protocol = null;
	this.host	= null;
	this.path	= null;
	this.urlBase = null;
	this.params = new stingray.util.hashList();
	this.search = null;
	this.parseURL();	
}
/**
 * An internal method that parses the raw url into a url base, and params
 * @param {_url} the url to parse
 */
stingray.util.urlParser.prototype.parseURL = function(_url) {	
	// set raw url if passed
	if (_url != null) {
		this.rawURL = _url;
	}
	if (this.rawURL != null) {
		// parse protocol
		var protocolIndex = this.rawURL.indexOf("//"); 
		if (protocolIndex > 0) {
			this.protocol = this.rawURL.substring(0,protocolIndex);
		}
		// parse host
		var startHost = 0;
		var endHostIndex = 0;
		if (protocolIndex > 0) {
			startHost = protocolIndex +2;
			endHostIndex = (this.rawURL.substr(startHost)).indexOf("/") + startHost;
		}
		if (endHostIndex > 0) {
			this.host = this.rawURL.substring(startHost,endHostIndex);
		}
		// parse QS
		var startQS = this.rawURL.indexOf("?");
		if (startQS > 0) {
			this.path = this.rawURL.substring(endHostIndex,startQS ); 
		} else if (startQS == -1) {
			//if the url is relative and there is no query string, then the entire url is the path
			if (endHostIndex > 0) {
				this.path = this.rawURL.substr(endHostIndex); 
			} else {
				this.path = this.rawURL;
			}
		}
		// initialize params
		this.params = new stingray.util.hashList();
		//look for '?' delimiter
		var urlArr = this.rawURL.split("?");
		this.urlBase = urlArr[0];
		if (urlArr.length > 1) {
			this.search = urlArr[1];
			if (this.search != null) {
				var qsArr = this.search.split("&");
				for (var i = 0; i < qsArr.length; i++) {
					var paramArr = qsArr[i].split("=");
					var name = paramArr[0];
					var value = "";
					if (paramArr.length > 1)
						value = paramArr[1];
					//split comma separated values
					//and assign params
					var valueArr = value.split(",");
					for (var n = 0; n < valueArr.length; n++) {
						this.params.addItem(name,valueArr[n]);
					}			
				}
			}
		}
	}
}
/**
 * gets an array of values for the key
 * @param {_name} the name of the key
 * @returns {Array} array of strings
 */
stingray.util.urlParser.prototype.getParamValues = function(_name) {	
	return this.params.getItemArray(_name);
}
/**
 * gets a comma separated string of values for the key
 * @param {_name} the name of the key
 * @returns {String} comma seperated list is returned for multiple values
 */
stingray.util.urlParser.prototype.getParamValue = function(_name) {	
	var valArray = this.params.getItemArray(_name);
	if (valArray != null && valArray.length > 0) {
		return valArray.join(",");
	} else {
		return "";
	}
}
/**
 * takes a single name and value, overwrites any existing value(s)
 * @param {_name} the key
 * @param {_value} the value
 */
stingray.util.urlParser.prototype.setParam = function(_name,_value) {
	this.params.setItem(_name,_value);
}
/**
 * takes a single name and value, appends to any existing value
 * @param {_name} the key
 * @param {_value} the value
 */
stingray.util.urlParser.prototype.addParam = function(_name,_value) {
	this.params.addItem(_name,_value);
}
/**
 * removes a param by name
 * @param {_name} the key
 */
stingray.util.urlParser.prototype.removeParam = function(_name,_value) {
	this.params.removeItem(_name);
}
/**
 * takes a url and sets all params from it
 * @param {_url} the url to parse values from
 */
stingray.util.urlParser.prototype.setParamsFromURL = function(_url) {
	var argURL;
	if (typeof _url == "string") {
		argURL = new stingray.util.urlParser(_url);
	} else {
		argURL = _url;
	}
	this.params.union(argURL.params);
}
/**
 * clears params
 */
stingray.util.urlParser.prototype.clearParams = function(_name) {
	this.params = new stingray.util.hashList();
}
/**
 * constructs a url from the member variables, if protocol and host are set
 * will return a fully qual'ed url, otherwise it's relative
 */
stingray.util.urlParser.prototype.getURLString = function() {
	var result = new stingray.util.stringBuffer();
	//absolute or relative url...
	if (this.protocol != null && this.host != null) {
		result.append(this.protocol + "//" + this.host);
	}
	if (this.path != null) {
		result.append(this.path  + "?");
	}
	result.append(this.params.toString());
	return result.toString();
}


/**
 * hashed/indexed object collection
 * @class stingray.util.hashList
 * @author brian@stingrayengineering.com
 * @constructor
 * @param _args {Array} an optional array of name/value pairs to populate the hash
 */
stingray.util.hashList = function() {
	this.hashIndex = new Array();
	this.init(arguments);
}
/**
 * initialize the collection with any arguments passed in
 * @param _args {Array} optional array of name/value pairs to populate the hash
 */
stingray.util.hashList.prototype.init = function(_args) {
	if (_args != null) {
		// is _args a single array or a list of comma separated name/value pairs?
		if (_args.length == 1 && _args[0] instanceof Array) {
			_args = _args[0];
		}
		var name;
		var value;
		for(var i = 0; i < _args.length; i++) {
			name = null;
			value = null;
			name = _args[i];
			if (i < _args.length - 1) {
				i++;
				value = _args[i];
			}
			// name and value, populate collection
			if (name != null && value != null) {
				this.addItem(name,value);
			}
		}
	}
}
/**
 * returns the length of the collection
 * @return {Int} collection length
 */
stingray.util.hashList.prototype.getLength = function() {		
	return this.hashIndex.length;
}
/**
 * returns boolean indicating whether or not an item by the passed in name exists in the collection
 * @param _name {String} name of the item to look for
 * @return {bool}
 */
stingray.util.hashList.prototype.hasItem = function(_name) {
	if ( this[_name] && ( this[_name] != null ) ) {
		return true;
	} else {
		return false;
	}
}
/**
 * adds an item to the collection. If the key exists, the value will be appended to the "multi-value"
 * value collection.  To overwrite/replace an item, use the 'setItem' method.
 * @param _name {String} name of the item to add
 * @param _value {Object} item to add
 * @see #setItem
*/
stingray.util.hashList.prototype.addItem = function(_name,_value) {
	if (!this.hasItem(_name)) {
		this.setItem(_name,_value);
	} else {
		var itemArray = this.getItemArray(_name);
		itemArray[itemArray.length] = _value;
	}
}
/**
 * adds an item to the collection.  If the key exists, value will overwrite any existing value(s).
 * To preserve and append to the existing values, use the 'addItem' method.
 * @param _name {String} name of the item to add
 * @param _value {Object} item to add
 * @see #addItem
 */
stingray.util.hashList.prototype.setItem = function(_name,_value)
{
	var hasItem = this.hasItem(_name);
	this[_name] = new Array()
	this[_name][0] = _value;
	if (!hasItem) {
		this.hashIndex[this.getLength()] =_name;
	}
}
/**
 * getItem will always return the 0 index item value.
 * When dealing with multi-valued keys, always use the getItemArray method.
 * @param _name {String} name of the item to fetch
 * @see #getItemArray
 */
stingray.util.hashList.prototype.getItem = function(_name) {
	var result = this.getItemArray(_name);
	if (result != null) {
		return result[0];
	} else {
		return result;
	}
}
/**
 * returns an array of all values for the key
 * @param _name {String} name of the multi-valued item
 */
stingray.util.hashList.prototype.getItemArray = function(_name) {
	if ( this.hasItem(_name)) {
		return this[_name];
	} else {
		return null;
	}
}
/**
 * getItemByIndex will always return the 0 index item value.  When dealing with 
 * multi-valued keys, always use the getItemArrayByIndex method.
 * @param _index {Int} index of item to retrieve
 * @see #getItemArrayByIndex
 */
stingray.util.hashList.prototype.getItemByIndex = function(_index) {
	var result = this.getItemArrayByIndex(_index);
	if (result != null) {
		return result[0];
	} else {
		return result;
	}
}
/**
 * returns an array of all values for the key.
 * @param _index {Int} index of item to retrieve
 */
stingray.util.hashList.prototype.getItemArrayByIndex = function(_index) {
	if (_index < this.getLength()) {
		return this[this.hashIndex[_index]];
	} else {
		return null;
	}
}	
/**
 * removes specified item from the collection
 * @param _name {String} key of the item to remove
 */
stingray.util.hashList.prototype.removeItem = function(_name) {
	if ( this.hasItem(_name) ) {
		var ind = this.getItemIndex(_name);
		this[_name] = null;
		this.hashIndex.splice(ind, 1);
	}
}
/**
 * removes specified item from the collection
 * @param _index {Int} index of item to remove
 */
stingray.util.hashList.prototype.removeItemByIndex = function(_index) {
	if ( _index < this.hashIndex.length ) {
		this.removeItem( this.hashIndex[_index] );
	}
}
/**
 * returns the index of the key, if no item is found, -1 is returned.
 * @param _name {String} key of the item to get the index of
 * @return {Int} item index
 */
stingray.util.hashList.prototype.getItemIndex = function(_name) {
	if ( this.hasItem(_name) ) {
		for (var i = 0; i < this.hashIndex.length; i++) {
			if (this.hashIndex[i] == _name) {
				return i;
			}
		}
	}
	return -1;
}
/**
 * returns the key at the index
 * @param _index {Int} index of the item to return the key of
 * @return {String} key
 */
stingray.util.hashList.prototype.getKey = function(_index) {
	if (_index < this.getLength()) {
		return this.hashIndex[_index];
	} else {
		return null;
	}
}
/**
 * Outputs the hash to an array, uses only 0 index of each value, to
 * preserve multi-valued keys, use toMultiValArray()
 * @return {Array} array of items
 * @see #toMultiValArray
 */
stingray.util.hashList.prototype.toArray = function() {
	var result = new Array();
	for (var i = 0; i < this.getLength(); i++) {
		result[i] = this.getItemByIndex(i);
	}
	return result;
}
/**
 * Outputs the hash to a two-dimensional array.
 * @return {Array}{Array} table of names/values
 */
stingray.util.hashList.prototype.toMultiValArray = function() {
	var result = new Array();
	for (var i = 0; i < this.getLength(); i++) {
		result[i] = this.getItemArrayByIndex(i);
	}
	return result;
}
/**
 * Merge another hashList with self. hashList argument will overwrite any existing items with like key.
 * @param _hash {stingray.util.hashList} hashList to merge in
 * @param _filter {Array} mask of keys, optional, applied to _hash
 */
stingray.util.hashList.prototype.union = function(_hash, _filter) {
	if (_hash != null) {
		try {
			var hashArray = _hash.toMultiValArray();
			if (hashArray != null) {
				for (var i = 0; i < hashArray.length; i++) {
					if (_filter == null) {
						this.setItem(_hash.getKey(i),hashArray[i]);
					} else {
						// only items which match the filter are used
						for (var fi = 0; fi < _filter.length; fi++) {
							if (_hash.getKey(i) == _filter[fi]) {
								this.setItem(_hash.getKey(i),hashArray[i]);
								break;
							}	
						}
					}
				}
			}
		} catch (ex) {
			RTSTRACER.addError(ex, "RTSHASH", "union","An error occured : " + ex.message);
		}
	}
}
/**
 * Outputs a string representation of the names/values of the hash
 */
stingray.util.hashList.prototype.toString = function() {
	var result = new stingray.util.stringBuffer();
	var paramsArr = this.toMultiValArray();
	for (var i = 0; i < paramsArr.length; i++) {
		result.append(this.getKey(i));
		result.append('=');
		result.append(paramsArr[i].join(",") );
		if (i < paramsArr.length -1) {
			result.append("&");
		}
	}
	return result.toString();	
}	


/**
 * imitates the java stringBuffer class
 * @class stingray.util.stringBuffer
 * @author brian@stingrayengineering.com
 * @constructor creates the internal buffer (array)
 */
stingray.util.stringBuffer = function() {
	this.buff = new Array();
}
/**
 * appends a string to the buffer
 * @param {String} _str string value to append to the buffer.
 */
stingray.util.stringBuffer.prototype.append = function(_str) {
	this.buff[this.buff.length] = _str;	
}
/**
 * appends a string to the buffer
 * @param {String[]} _strArr an array of strings
 */
stingray.util.stringBuffer.prototype.addArray = function(_strArr) {
	if ( _strArr != null ) {
		for (var i = 0; i < _strArr.length; i++) {
			this.append(_strArr[i]);
		}
	}
}
/**
 * clears the buffer
 */
stingray.util.stringBuffer.prototype.clear = function() {
	this.buff = new Array();
}
/**
 * indicate whether the buffer is empty, or has value(s)
 * @returns bool
 */
stingray.util.stringBuffer.prototype.hasValue = function() {
	return (this.buff != null && this.buff.length > 0);
}
/**
 * outputs contents of the buffer as a string
 * @returns {String}
 */
stingray.util.stringBuffer.prototype.toString = function(delim) {
	if ( delim == null ) delim = '';
	return this.buff.join(delim);
}


/*****************************    STRING PROTOTYPE ADDITIONS    *****************************/


/**
 * concat any number (accepts Array or string args) strings to the current
 */
String.prototype.concat = function() {
	var buf = new stingray.util.stringBuffer();
	buf.append(this);
	buf.addArray(arguments);
	return buf.toString();
}
/**
 * trim white space from the left hand side of the string
 */
String.prototype.lTrim = function() {
	return this.replace(/^\s*/, '');
}
/**
 * trim white space from the right hand side of the string
 */
String.prototype.rTrim = function() {
	return this.replace(/\s*$/, '');
}
/**
 * trim white space from both sides of the string
 */
String.prototype.trim = function() { 
	return this.lTrim().rTrim();
}
/**
 * test for a blank/null string
 */
String.prototype.isBlank =	function() {
	if (this == null) {
		return true;
	} else {
		var rxp = /(\S+)/;
		return !(rxp.test(this)); 
	}
}


/*****************************    ARRAY PROTOTYPE ADDITIONS    *****************************/


/**
 * concat any number (accepts Array or string args) strings to the current
 */
Array.prototype.reduce = function(itemFormatFunction) {
	var buf = new stingray.util.stringBuffer();
	for ( var i=0; i<this.length; i++ ) {
		buf.append(itemFormatFunction(this[i]));
	}
	return buf.toString();
}


