if (('coss' in window) == false) window.coss = {};

/**
 * Mimics class-based inheritance in Javascript.
 * You must call this function before assigning members.
 */
coss.extend = function (aSubClass, aSuperClass) {
	//create superclass instance that becomes the base of subclass instance
	aSubClass.prototype = new aSuperClass(aSuperClass);

	//reassign subclass's constructor as foremost
	aSubClass.prototype.constructor = aSubClass;
	
	//create reference to superclass
	aSubClass.superClass = aSuperClass;
	
	//create reference to superclass instance
	aSubClass.superProto = aSuperClass.prototype;
	
	/*//copy 'static' members from superclass to subclass
	for (var p in aSuperClass) {
		aSubClass[p] = aSuperClass[p];
	}*/
};

coss.selectOptionsByValue = function (aSelect, aValues, aMode) {
	if (aSelect.multiple == true) throw "Not supported... yet.";
	if (aMode) throw "Not supported... yet.";
	if (aValues != null && typeof aValues == 'object') throw "Not supported... yet.";
	
	var i;
	var value = aValues == null ? '' : aValues;
	var matchFound = false;
	
	for (i = 0; i < aSelect.options.length; i++) {
		if (aSelect.options[i].value == value) {
			aSelect.selectedIndex = i;
			matchFound = true;
			break;
		}
	}
	
	if (matchFound == false) {
		throw "Could not select value: '" + aValues + "'.";
	}
};

coss.toArray = function (aArray) {
	var k;
	var arr = [];
	for (k in aArray) {
		arr.push(aArray[k]);
	}
	return arr;
};

coss.mergeIntoSelect = function (aSelect, aOptions, aSort) {
	var options = ('length' in aOptions) ? aOptions : [aOptions];
	var i, s, el;
	for (i = 0; i < options.length; i++) {
		s = coss.array2dIndexOf(aSelect.options, 'value', options[i].value);
		if (s > -1) {
			if (aSelect.options[s].text != options[i].text) {
				aSelect.options[s].text = options[i].text;
			}
		}
		else {
			if ('parentNode' in options[i]) {
				aSelect.appendChild(options[i]);
			}
			else {
				el = aSelect.ownerDocument.createElement('option');
				aSelect.appendChild(el);
				el.text = options[i].text;
				el.value = options[i].value;
			}
		}
	}
};

coss.arrayGet = function (aArray, aPath, aLevelSeparator) {
	var levelSeparator = arguments.length >= 3 ? aLevelSeparator : '.';
	var pathParts = aPath.split(levelSeparator);
	var currentItem, i;
	
	currentItem = aArray;
	for (i = 0; i < pathParts.length; i++) {
		if (typeof currentItem == 'object' && currentItem != null && pathParts[i] in currentItem) {
			if (i == pathParts.length - 1) {
				return currentItem[pathParts[i]];
			}
			
			else {
				currentItem = currentItem[pathParts[i]];
			}
		}
		
		else {
			break;
		}
	}
	
	return null;
};

coss.arrayGetCol = function (aArray, aColName) {
	var values = [];
	var k;
	
	for (k in aArray) {
		values.push(aArray[k][aColName]);
	}
	
	return values;
};

coss.arrayIndexOf = function (aArray, aValue) {
	var i;
	
	if (aArray.indexOf) return aArray.indexOf(aValue);
	
	for (i = 0; i < aArray.length; i++) {
		if (aArray[i] == aValue) {
			return i;
		}	
	}
	
	return -1;
};

coss.array2dIndexOf = function (aArray, aSearchKey, aValue, aStrict) {
	var strict = arguments.length >= 4 ? aStrict : false;
	var i;
	
	for (i = aArray.length - 1; i >= 0; i--) {
		if ((strict == false && aArray[i][aSearchKey] == aValue) || (strict == true && aArray[i][aSearchKey] === aValue))
			break;
	}
	   
	return i;
};

/**
 * Ensures the specified value is safe to use as the value of a form element.
 * For example, the value '' will returned if null is passed, instead of 'null'.
 * @aValue object Value to be made safe.
 * @return Safe value.
 */
coss.makeFormSafe = function (aValue) {
	if (aValue === null || aValue === undefined) return '';
	return aValue + '';
};


coss.attachElementBinding = function (aElement, aBinding) {
	var k;
	
	if (('_coss_syncBindingEventListeners' in aElement) == false) {
		aElement._coss_syncBindingEventListeners = [];
		aElement.coss_syncBindings = coss.attachElementBinding._syncBindings;
	}
	
	for (k in aBinding.implementation) {
		if (k != 'onBindingAttached' && k != 'onSyncBinding') {
			aElement[k] = aBinding.implementation[k];
		}
	}
	
	if ('onSyncBinding' in aBinding.implementation) {
		aElement._coss_syncBindingEventListeners.push(aBinding.implementation.onSyncBinding);
	}
	
	if ('onBindingAttached' in aBinding.implementation) {
		aBinding.implementation.onBindingAttached.apply(aElement, [{
			binding : aBinding
		}]);
	}
	
	return aElement;
};
coss.attachElementBinding._syncBindings = function () {
	var i;
	for (i = 0; i < this._coss_syncBindingEventListeners.length; i++) {
		this._coss_syncBindingEventListeners[i].apply(this, []);
	}
};

coss.urlDecode = function (str) {
    return decodeURIComponent((str+'').replace(/\+/g, '%20'));
}

/**
 * @class HashManager.
 */

coss.HashManager = function () {
	this._data = {};	
};

coss.HashManager.prototype._load = function () {
	var data = null;
	
	if (window.location.hash.substr(1,1) == '{') {
		try {
			data = JSON.parse(window.location.hash.replace(/^#/, ''));
		}
		catch (ex) {}
	}

	if (data == null) data = {};
	this._data = data;
};

coss.HashManager.prototype._save = function () {
	window.location.hash = JSON.stringify(this._data);
};

coss.HashManager.prototype.get = function (aKey) {
	var value = '';
	this._load();
	if (aKey in this._data) value = this._data[aKey];
	return value;
};

coss.HashManager.prototype.set = function (aKey, aValue) {
	this._load();
	this._data[aKey] = aValue;
	this._save();
};

coss.HashManager.prototype.remove = function (aKey) {
	this._load();
	if (aKey in this._data) {
		delete this._data[aKey];		
	}
	this._save();
};

