﻿


///////// 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 'manheim' 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.manheim && (typeof _global.manheim != "object" || _global.manheim.NAME)) throw new Error("GlobalNamespace 'manheim' already exists in an incompatible format and will be overridden");
			
			
			/* Create base namespace object */
			_global.manheim = {};
			
			_global.manheim.NAME = "manheim";
			_global.manheim.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.manheim.Global = Object.subClass(
					
						IN PLACE OF
					
						var Global = Object.subClass(
			 *
			 *********************************
			 */
			
			var GlobalUtilities = Object.subClass(
				{
					init : function () 
					{
						this._className		= "manheim.GlobalUtilities";
						this._namespaces	= { "manheim" : "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);
						});
						
					},
					
					/*
					className : "manheim.Global",
					
					namespaces : { "manheim" : "1.0" },
					
					classes : { this.className : true },
					*/
					
					createNamespace : function (name, version)
					{
						// Check name exists 
						if (!name) throw new Error("manheim.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("manheim.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.manheim;
						
						for(var i = 0; i < levels.length; i++) 
						{
							var level = levels[i];
							
							//If namespace fully resolved ie manheim.a.b... instead of a.b... skip first level
							if (level != "manheim")
							{
								// 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("manheim.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("manheim.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;
					},
					
					
					isNamespaceDefined : function (name)
					{
						if (!name) throw new Error("manheim.Global.isNamespaceDefined() : you must supply a name");
						return name in this._namespaces;
					},
					
					
					/**
					 * Method for checking 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("manheim.Global.isClassDefined() : you must supply a name");
						// 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("manheim.Global.isClassDefined((): 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);
						//alert("ns = " + ns + " // c = " + c);
						
						//Check if namespace has been defined
						if (!this.isNamespaceDefined(ns))
						{
							return false;
						}
						else 
						{
							// Resolve namespace and test
							nsObj = this.resolveStringToNamespaceObject(ns);
							return c in nsObj;
						}
					},
					
					
					
					/**
					 * Method for resolving a period (".") delimited Namespace string 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("manheim.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;
					},
					
					
					
					/**
					 * Method for resolving a period (".") delimited Namespace string 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("manheim.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
							nsObj = this.resolveStringToNamespaceObject(ns);
							
							if (c in nsObj) return nsObj[c];
							else 
							{
								alert("ouch : c = " + c + " // " + nsObj[c]);
								return false;
							}
						}
						
					},
					
					
					checkNamespaceVersion : function (name)
					{
						if (typeof name != "string") throw new Error("manheim.Global.checkNamespaceVersion() name '" + name + "' is not a string");
						if (!this.isNamespaceDefined(name)) throw new Error("manheim.Global.checkNamespaceVersion() : The namespace '" + name + "' is not defined");
						
						return this._namespaces[name];
					},
					
					
					isPageLoaded : function ()
					{
						return this._pageLoaded;
					},
					
					
					setPageLoadedStatus : function (b)
					{
						this._pageLoaded = b;
					}
					
				}
				
			);
			
			//Create instance of 'GlobalUtilities' class
			//Create 'manheim.portfolio' Namespace
			//_global.mrm.global = new Global();
			//_global.mrm.global.createNamespace("manheim.portfolio");
			
			try {
				_global.mrm.global = new GlobalUtilities();
				_global.mrm.global.createNamespace("manheim.portfolio", "1.0");
			}
			catch (e)
			{
				alert("!! WARNING !! \n" + e.message);
			}
			
			
		})(this);
	
	
	//alert("1: " + manheim.portfolio);
	
	
	
	//alert(this.mrm.global.namespaces["manheim.portfolio"]);
	//alert(this.mrm.global.isNamespaceDefined("manheim.portfolio"));
	//alert(this.mrm.global.checkNamespaceVersion("manheim.portfolio"));
	//alert("this should be false (isNamespaceDefined) : " + this.mrm.global.isNamespaceDefined("aaa"));
	
