String.prototype.toTitleCase = function()
{
	var matches = /(\S+)/g.match(this);
	if (!matches || matches.length < 2) { return this; }
	for (var idx = 1, len = matches.length, match; idx < len; idx++)
	{
		matches[idx] = matches[idx].charAt(0).toUpperCase() + matches[idx].substr(1).toLowerCase();
	}
	return matches.join(' ');
};

String.prototype.unencodeHTML = function()
{
	return jQuery('<div>' + this + '</div>').text();
};

String.prototype.encodeHTML = function()
{
	return jQuery('<div></div>').text(this).text();
};

/**
 * Creates a delegate to the current function which, when called, will get called in the scope you supply
 * Note:	1) Arguments passed into the delegated function (which this returns) will the first arguments
 *			2) The delegated function, when called, will be called in the global scope if no scope is supplied
 *			3) 
 */
Function.prototype.delegate = function(scope)
{
	var func = this;
	if (arguments.length < 2)
	{
		return function() { func.apply(scope||this, arguments); };
	}
	else
	{
		var args = Array.prototype.slice.apply(arguments, [1]);
		return function() { func.apply(scope||this, jQuery.makeArray(arguments).concat(args)); };
	}
};

if (!window.console) { window.console = { log: function(){} }; }

jQuery.noConflict();

/**
 * Global X (Genex) namespace
 */
var X = {};

/**
 * Creates a Namespace
 */
X.ns = function ns(namespace, value, root)
{
	var parent = root || window;
	var namespaces = namespace.split('.');
	var len = namespaces.length;
	var name;
	if (len > 1)
	{
		len--;
		for (var idx = 0; idx < len; idx++)
		{
			name = namespaces[idx];
			if (!parent[name]) { parent[name] = {}; }
			parent = parent[name];
		}
		name = namespaces[len];
	}
	else
	{
		name = namespace;
	}
	
	if (typeof(value) !== 'undefined')
	{
		parent[name] = value || {};
	}
	return parent[name];
};

/**
 * Creates an inheritance chain
 */
X.ns('X.inherits', function inherits(subClass, baseClass)
{
	var newConstructor = function()
	{
		this.base = function()
		{
			baseClass.apply(this, arguments);
			this.base = baseClass.prototype;
		}

		subClass.apply(this, arguments);
	};
	newConstructor.prototype = { constructor: newConstructor };

	for (var memberName in baseClass.prototype)
	{
		if (memberName === 'constructor' || memberName === 'base') { continue; }
		newConstructor.prototype[memberName] = baseClass.prototype[memberName];
	}
	for (var memberName in subClass.prototype)
	{
		if (memberName === 'constructor' || memberName === 'base') { continue; }
		newConstructor.prototype[memberName] = subClass.prototype[memberName];
	}
	return newConstructor;
});

/**
 * Creates a Class
 */
X.ns('X.createClass',
function createClass(namespace, constructor, proto, staticMembers, baseClass)
{
	if (typeof(namespace) !== 'string' || namespace.length === 0) { throw new Error('Invalid namespace argument'); }
	if (typeof(constructor) !== 'function') { throw new Error('Invalid constructor argument'); }
	if (proto)
	{
		constructor.prototype = proto;
		constructor.prototype.constructor = constructor;
	}
	if (typeof(baseClass) === 'function')
	{
		constructor = X.inherits(constructor, baseClass);
	}
	if (staticMembers)
	{
		jQuery.extend(constructor, staticMembers);
	}
	return X.ns(namespace, constructor);
});

/**
 * Creates a Singleton
 */
X.ns('X.createSingleton',
function createSingleton(namespace, constructor, proto, baseClass)
{
	if (typeof(namespace) !== 'string' || namespace.length === 0) { throw new Error('Invalid namespace argument'); }
	if (typeof(constructor) !== 'function') { throw new Error('Invalid constructor argument'); }
	if (proto)
	{
		constructor.prototype = proto;
		constructor.prototype.constructor = constructor;
	}
	if (typeof(baseClass) === 'function')
	{
		constructor = X.inherits(constructor, baseClass);
	}
	var singleton = new constructor();
	return X.ns(namespace, singleton);
});

/**
 * CLASS: Event
 * @param type	String	Type of event
 * @param props	Object	Key value pairs to extend this object with
 */
X.createClass('X.Event',
	// Constructor
	function XEvent(type, target)
	{
		this.type = type;
		this.target = target||{};
		return this;
	},
	// Prototype Members
	null,
	// Static Members
	{
		isTypeValid: function(eventClass, type)
		{
			var typeIsValid = false;
			for (var prop in eventClass)
			{
				if (eventClass[prop] === type) { typeIsValid = true; break; }
			}
			return typeIsValid;
		}
	},
	// Base Class
	null
);

/**
 * CLASS: EventDispatcher
 */
X.createClass('X.EventDispatcher',
	// Constructor
	function XEventDispatcher()
	{
		this.__events = {};
		return this;
	},
	// Prototype Members
	{
		addEventListener: function(type, listener)
		{
			if (typeof(type) !== 'string' || type.length === 0) { throw new Error('Invalid event type. Expected a non-zero length string.'); }
			if (typeof(listener) !== 'function') { throw new Error('Invalid event listener. Expected a function.'); }
			if (!this.__events[type])
			{
				this.__events[type] = [];
			}
			this.__events[type].push(listener);
		},
		removeEventListener: function(type, listener)
		{
			var listeners = this.__events[type];
			if (!listeners) { return; }
			for (var idx = 0, len = listeners.length; idx < len; idx++)
			{
				if (listeners[idx] !== listener) { continue; }
				listeners.splice(idx, 1);
				break;
			}
		},
		dispatchEvent: function(evt)
		{
			if (!this.__events || !evt || !evt.type || !evt.target) { throw new Error('Invalid event. Expected an Event object.'); }
			var listeners = this.__events[evt.type];
			if (!listeners) { return; }
			evt.target = this;
			for (var idx = 0, len = listeners.length; idx < len; idx++)
			{
				listeners[idx](evt);
			}
		}
	}
);

// Set clean up handler
jQuery(window).unload(function(evt)
{
    jQuery(document.body).empty();
	X = jQuery = null;
    if (window.CollectGarbage) { window.CollectGarbage(); }
});

jQuery.browser.webkit = false;
jQuery.browser.chrome = false;
if (/AppleWebKit/g.test(navigator.userAgent))
{
	jQuery.browser.webkit = true;
	if (/Chrome/g.test(navigator.userAgent))
	{
		jQuery.browser.safari = false;
		jQuery.browser.chrome = true;
	}
}

