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

coss.HINT_YOUTUBE_PASS_THROUGH = 1;

/**
 * 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.getHint = function (aHint) {
	if (aHint == coss.HINT_YOUTUBE_PASS_THROUGH) {
		if (
			window.navigator.userAgent.indexOf('Android') > -1
			|| window.navigator.userAgent.indexOf('iPhone') > -1
			|| window.navigator.userAgent.indexOf('iPad') > -1
		)
		return true;
		return false;
	}
	
	throw "Unknown hint: '" + aHint + "'";
};

coss.selectOptionsByValue = function (aSelect, aValues, aMode) {
	if (!('options' in aSelect)) return coss.selectOptionsByValue._checkboxes.apply(this, arguments);
	
	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.selectOptionsByValue._checkboxes = function (aGroup, aValues, aMode) {
	if (aValues != null && typeof aValues == 'object') throw "Not supported... yet.";
	
	var i;
	var group = 'length' in aGroup ? aGroup : [aGroup];
	var value = aValues == null ? '' : aValues;
	
	for (i = 0; i < group.length; i++) {
		if (group[i].value == value) {
			group[i].checked = true;
		}
		else {
			group[i].checked = false;
		}
	}
};

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

coss.setClassName = function (aElement, aClassName, aAddOrRemove) {
	if (aAddOrRemove == true) $(aElement).addClass(aClassName);
	else $(aElement).removeClass(aClassName);
};

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, aOptions) {
	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
		  },
		  
		  aOptions
		]);
	}
	
	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'));
};

coss.getMonthName = function ( aDate ) {
	var aMonth = ( typeof aDate == 'object' ) ? aDate.getMonth( ) : parseInt( aDate );
	var months = [];

	months[0] = 'January';
	months[1] = 'February';
	months[2] = 'March'; 
	months[3] = 'April';
	months[4] = 'May';
	months[5] = 'June';
	months[6] = 'July';
	months[7] = 'August';
	months[8] = 'September';
	months[9] = 'October';
	months[10] = 'November';
	months[11] = 'December';

	return ( aMonth in months ) ? months[ aMonth ] : false;
};

/**
 * @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();
};


coss.HashMap = function () {
	if (!(arguments.length == 1 && arguments[0] == coss.HashMap)) this.construct.apply(this, arguments);
};

coss.HashMap.prototype.construct = function (aData) {
	var k;
	this._data = {};
	if (aData) {
		for (k in aData) {
			this._data[k] = aData[k];
		}
	}
};

coss.HashMap.prototype.getData = function () {
	return this._data;
};

coss.HashMap.prototype.get = function (aPath) {
	return coss.arrayGet(this._data, aPath);
};

coss.HashMap.prototype.getAsString = function (aPath) {
	var value = this.get(aPath);
	if (value === null || value === undefined) value = '';
	else value = value.toString();
	return value;
};

coss.HashMap.prototype.set = function (aPath, aObject) {
	var levelSeparator = '.';
	
	if (aPath.indexOf(levelSeparator) > -1) {
		throw "Setting values via multi-level paths is not supported yet.";
	}
	
	this._data[aPath+''] = aObject;
};
