/// <reference path="C:\References\jquery-1.4.1-vsdoc.js" />

// ********************************************************************
// 
// Manheim Retail Marketing
// ********************************************************************
// Copyright © 2010 Manheim Retail Marketing.
//
//  Summary
// ******************************
// $Workfile: ClassExtension.js $
// Notes:
//
// File History Information
// ************************				
// Created on:					    
// Last Modified:					$Modtime: 19/11/10 9:39 $
// Original Author:					Liam Prescott
// Last Modified by:				$Author: Prescottl $
// Last JS Lint:					N/A
//
// Source Control Information
// **************************
// File Version:					$Revision: 8 $
// VSS Location:					$Archive: /Manheim.Portfolio/Manheim.Portfolio.Web.UI.Client.Assets/assets/js/mrm/lib/core/1.0/ClassExtension.js $
//
// ********************************************************************


///////// CLASSES ARE CREATED 'INSIDE' NAMESPACES

///////// THEREFORE : To create class : 
										// a. createNamespace / check for existance 
										// b. Create class definition within namespace

/**
 *
 * CORE LIBRARY FUCTIONALITY
 *
 */

	/**
	 * CLASS EXTENSION FUNCTIONALITY
	 *	
	 * Self initialising utility that extends the javascript 'Object' class with the addition of a 
	 * new CLASS LEVEL method '.subClass()' that facilitates creating OOP orientated class structures
	 *
	 * The first level class of ANY HEIRACHY should extend Object:
	 *
	 ****************************************************************************************************
	 EXAMPLE :
	 
			CLASS DEFINITIONS:
			
			var a = Object.subClass(
				{
					init	: function () {}, //// This is the class constructor method
					method1 : function (a) {}, //// Class prototype methods
					method2 : function () {}
				}
			);
			
			var b = a.subClass(
				{
					init		: function () {},
					method1		: function (a,b) { this._super(a) }, //// Override method calling superclasses version of same method
					newMethod1	: function () {}
				}
			);
			
			
			CLASS USAGE:
			
			var instance_a = new a();
			var instance_b = new b();
			
		
			CLASS METHODS: 
			Class methods can be added either after creation or during constructor method

	 ****************************************************************************************************
	 * 
	 * NOTE : This does NOT affect Object.prototype therefore shouldn't cause the associated problems of doing so
	 *
	 *
	 *	- See :	http://ejohn.org/blog/simple-javascript-inheritance/
	 *			http://jsninja.com/Function_Prototypes
	 *
	 */
 
(function () {
	
	var initializing = false;

	// Determine if functions can be serialized
	var fnTest = /xyz/.test(function () {xyz; }) ? /\b_super\b/ : /.*/;
	
	// Create a new Class that inherits from this class
	Object.subClass = function (prop) 
	{
		var _super = this.prototype;
		
		// Instantiate a base class (but only create the instance and don't run the init constructor)
		initializing	= true;
		var proto		= new this();
		initializing	= false;
		
		// Copy the properties over onto the new prototype
		for (var name in prop) {
			// Check if we're overwriting an existing function
			proto[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function (name, fn)
			{
				return function ()
				{
					var tmp = this._super;
					
					// Add a new ._super() method that is the same method but on the super-class
					this._super = _super[name];
					
					// The method only need to be bound temporarily, so we remove it when we're done executing
					var ret		= fn.apply(this, arguments);
					this._super = tmp;
					
					return ret;
				};
			})(name, prop[name]) : prop[name];
		}
		
		// Dummy class constructor
		function Class()
		{
			// All construction is actually done in the init method
			if (!initializing && this.init)
			{
				this.init.apply(this, arguments);
			}
		}
		
		// Populate our constructed prototype object
		Class.prototype = proto;
		
		// Enforce the constructor
		Class.constructor = Class;
		
		// And make this class extendable
		Class.subClass = arguments.callee;
		
		return Class;
	};
})();






/**
 * GLOBAL UTILITY CLASS instance creation
 *
 * Self initialising GLOBAL UTILITY CLASS instance
 *
 * - Creates base global namespace object 'mrm' into which all other namespaces and classes are created
 *
 * - Provides a global utilities object (accessed via global object 'mrm.global') that provides a series generic core utility methods:
 *		- createNamespace(name)
 *		- isNamespaceDefined(name)
 *		- checkNamespaceVersion(name)
 */
	
(function (globalNamespace)
{
	
	var _global = globalNamespace;
	
	if (_global.mrm && (typeof _global.mrm !== "object" || _global.mrm.NAME))
	{
		throw new Error("GlobalNamespace 'mrm' already exists in an incompatible format and will be overridden");
	}
		
	
	/* Create base namespace object */
	_global.mrm = {};
	
	_global.mrm.NAME = "manheim retail marketing";
	_global.mrm.VERSION = "1.0";
	
	
	/**
	 *********************************
	 * DEFINE 'Global' class 
	 * 
	 * Transient declaration : declare, use and destroy as currently only require single instance
	 *
	 * NOTE:	If in future wish to add CLASS LEVEL METHODS:
				Define in class in global scope via :
	 
				_global.mrm.Global = Object.subClass(
			
				IN PLACE OF
			
				var Global = Object.subClass(
	 *
	 *********************************
	 */
	
	var GlobalUtilities = Object.subClass
	({
		/*
		=============================
		CONSTRUCTOR
		=============================
		*/
			"init" : function () 
			{
				this._className		= "mrm.GlobalUtilities";
				this._namespaces	= { "mrm" : "1.0" };
				//this._classes		= { this.className : "1.0"};
				
				//Page loaded status
				this._pageLoaded	= false;
				
				
				//Set page loaded status on document .ready()
				$(document).ready(function () {
					mrm.global.setPageLoadedStatus(true);
				});
			},
		
		
		
		/*
		=============================
		PUBLIC METHODS
		=============================
		*/
			// Create a new namespace
			//
			// @param : name [String] : Period '.' delimited Namespace string
			// @param : version [String] : Version number (this allows the ability to check whether a specific version of a class exists)
			"createNamespace" : function (name, version)
			{
				// Check name exists 
				if (!name)
				{
					throw new Error("mrm.Global.createNamespace(): name required");
				}
				
				// Check name doesn't begin or end with a period or contain two periods in a row
				if (name.charAt(0) === '.' || name.charAt(name.length - 1) === '.' || name.indexOf("..") !== -1)
				{
					throw new Error("mrm.Global.createNamespace((): illegal name: " + name);
				}
			
				// Break the name at periods and create an array of levels (the object hierarchy)
				var levels = name.split('.');

				// For each namespace component, either create an object or ensure that an object by that name already exists.
				var container = _global.mrm;
				
				for (var i = 0; i < levels.length; i++)
				{
					var level = levels[i];
					
					//If namespace fully resolved ie mrm.a.b... instead of a.b... skip first level
					if (level !== "mrm")
					{
						// If there is no property of container with this name, create an empty object.
						if (!container[level])
						{
							container[level] = {};
						}
						// Else if there is already a property, make sure it is an object
						else if (typeof container[level] !== "object") 
						{
							var n = levels.slice(0, i).join('.');
							throw new Error("mrm.Global.createNamespace() : " + n + " already exists and is not an object");
						}
						container = container[level];
					}
				}

				// The last container traversed above is the namespace created.
				var namespace = container;

				// It is an error to define a namespace twice. It is okay if the namespace object already exists, but it must not already have a NAME property defined.
				if (namespace.NAME)
				{
					throw new Error("mrm.Global.createNamespace() : " + name  + " is already defined");
				}

				// Initialize name and version fields of the namespace
				namespace.NAME		= name;
				namespace.VERSION	= (version) ? version : "0.0";

				// Register this namespace and version number
				this._namespaces[name] = namespace.VERSION;
				//this._namespaces[name] = namespace;

				// Return the namespace object to the caller
				return namespace;
			},
			
			
			// Check if namespace has been defined
			//
			// @param : name [String] : Period (".") delimited Namespace string
			// @return : [Boolean]
			"isNamespaceDefined" : function (name)
			{
				if (!name)
				{
					throw new Error("mrm.Global.isNamespaceDefined() : you must supply a name");
				}
				return name in this._namespaces;
			},
			
			
			// Check if either a 'class definition' OR a 'class instance' has been created
			//
			// @param	: name [String]	: Period (".") delimited class path
			// @return	: [Boolean]
			"isClassDefined" : function (name)
			{
				if (!name)
				{
					throw new Error("mrm.Global.isClassDefined() : you must supply a name");
					return false;
				}
				
				// Check name doesn't begin or end with a period or contain two periods in a row and contains a namespace : ie. contains AT LEAST one instance of (".") e.g. ("a.B" = valid) whereas ("B" = invalid)
				if (name.charAt(0) === '.' || name.charAt(name.length - 1) === '.' || name.indexOf("..") !== -1 || name.indexOf(".") === -1)
				{
					throw new Error("mrm.Global.isClassDefined((): illegal name: " + name);
					return false;
				}
				
				// Extract namespace and Class
				var lastindex = name.lastIndexOf(".");
				
				var ns	= name.slice(0, lastindex);
				var c	= name.slice(lastindex + 1, name.length);
				//alert("ns = " + ns + " // c = " + c);
				
				//Check if namespace has been defined
				if (!this.isNamespaceDefined(ns))
				{
					return false;
				}
				else 
				{
					// Resolve namespace and test
					var nsObj = this.resolveStringToNamespaceObject(ns);
					return c in nsObj;
				}
			},
			
			
			// Check if either a 'class instance' has been created
			//
			// @param	: name [String] : Period (".") delimited class 
			//
			// @return	: [Boolean]
			"isClassInstanceDefined" : function (name)
			{
				return this.isClassDefined(name)
			},
			
			
			// Resolve a period (".") delimited Namespace string for a NAMESPACE to an object reference
			//
			// @param	: namespaceString [String] : Period (".") delimited class path : THIS MUST BE A VALID NAMESPACE STRING - use this.isNamespaceDefined() to test
			//
			// @return	: [Object] : reference to namespace object
			"resolveStringToNamespaceObject" : function (name)
			{
				// Check name doesn't begin or end with a period or contain two periods in a row
				if (name.charAt(0) === '.' || name.charAt(name.length - 1) === '.' || name.indexOf("..") !== -1 || name.indexOf(".") === -1)
				{
					throw new Error("mrm.Global.resolveStringToNamespaceObject((): illegal name: " + name);
				}
				
				var s = name;
				if (!this.isNamespaceDefined(s))
				{
					return false;
				}
				
				var a	= s.split(".");
				var objRef = _global;
				
				for (var i = 0; i < a.length; i++)
				{
					var c	= a[i];
					objRef	= objRef[c]; 
				}
				
				return objRef;
			},
			
			
			// Resolve a period (".") delimited Namespace string for a CLASS INSTANCE to an object reference
			//
			// @param	: namespaceString [String] : Period (".") delimited class path : THIS MUST BE A VALID NAMESPACE STRING - use this.isNamespaceDefined() to test
			// @return	: [Object] : reference to namespace object
			"resolveStringToClassInstanceObject" : function (name)
			{
				// Check name doesn't begin or end with a period or contain two periods in a row
				if (name.charAt(0) === '.' || name.charAt(name.length - 1) === '.' || name.indexOf("..") !== -1 || name.indexOf(".") === -1)
				{
					throw new Error("mrm.Global.resolveStringToNamespaceObject((): illegal name: " + name);
				}
				
				// Extract namespace and Class
				var lastindex = name.lastIndexOf(".");
				
				var ns	= name.slice(0, lastindex);
				var c	= name.slice(lastindex + 1, name.length);
				
				//Check if namespace has been defined
				if (!this.isNamespaceDefined(ns))
				{
					return false;
				}
				else 
				{
					// Resolve namespace and test
					var nsObj = this.resolveStringToNamespaceObject(ns);
					
					if (c in nsObj)
					{
						return nsObj[c];
					}
					else 
					{
						alert("ouch : c = " + c + " // " + nsObj[c]);
						return false;
					}
				}
			},
						
			
			// @param	[String]
			// @return	[Object]
			"checkNamespaceVersion" : function (name)
			{
				if (typeof name !== "string")
				{
					throw new Error("mrm.Global.checkNamespaceVersion() name '" + name + "' is not a string");
				}
				
				if (!this.isNamespaceDefined(name))
				{
					throw new Error("mrm.Global.checkNamespaceVersion() : The namespace '" + name + "' is not defined");
				}
				
				return this._namespaces[name];
			},
			
			
			// Check whether page has loaded : 'this._pageLoaded' is automatically set to true on document ready
			//
			// @return : [Boolean]
			"isPageLoaded" : function ()
			{
				return this._pageLoaded;
			},
			
			
			// Set page loaded status
			//
			// @param : [Boolean]
			"setPageLoadedStatus" : function (b)
			{
				this._pageLoaded = b;
			},
			
			
			// Return QueryString parameters object 
			// Parses the QueryString parameters from the passed in url to an object of name-value pairings.
			//
			// @param : url [String] :
			//
			// @return : [Object] : Containing name value pairings e.g. { "arg1" : "prop1", "arg2" : "prop2", "arg3" : "prop3" } 
			"getQSParams" : function (url)
			{ 
				var args = new Object(); 
				if (url.indexOf("?") == 0) return args;
				
				var query = url.substring(url.indexOf("?") + 1);
				var pairs = query.split("&"); 
				for(var i = 0; i < pairs.length; i++)
				{ 
					var pos = pairs[i].indexOf("="); 
					if (pos == -1) continue; 
					var argname = pairs[i].substring(0,pos); 
					var value = pairs[i].substring(pos+1); 
					args[argname] = unescape(value); 
				} 
				return args; 
			},
			
			
			// Determine whether to @font-face css should be supported
			// LOGIC = Return TRUE, except in the following situation:
			//
			//	Operating system = Windows
			//	+
			//	Operating system version < Vista
			//	+
			//	(
			//		Browser !ie
			//		||
			//		Browser == ie && version < 7 
			//	)
			//	
			//
			// @return : [Boolean]
			"customCssFontSupport" : function ()
			{
				var support = true;
				var ua = navigator.userAgent;
								
				if (ua && (ua.toLowerCase().indexOf("win") != -1))
				{
					// This will run ONLY for windows browsers
					var i = ua.indexOf("Windows NT");
					// Valid flag
					var b = (i != -1) ? true : false;
					// If win NT test if valid against major version number
					// Major os verion
					if (b)
					{
						var v = Number(ua.slice(i+11, i+12));
						if (v < 6)
						{
							// If browser !ie
							if (!$.browser.msie) support = false;
							// Else if version < 7
							else if ($.browser.version < 7) support = false;
						}
					}
					// Else always false
					else support = false;
				}
				return support;
			}
			
			
	});
	
	
	
	try {
		_global.mrm.global = new GlobalUtilities();
	}
	catch (e)
	{
		alert("!! WARNING !! \n" + e.message);
	}
	
	
})(this);

