// Enable (pseudo-)namespaces for Javascript


/**
 * registerNamespace registers a fully-qualified namespace identifier in the form "x.y.z" as a hierarchal series of Javascript objects representing the various parts of the identifier.  If part of the hierarchy already exists, these objects are used instead of creating more.
 * @param {String} fullyqualifiednsid A string of the form "x.y.z", representing the namespace objects to be created.
 * @returns {Boolean} true 
 */
function registerNamespace(fullyqualifiednsid)
{
	// Split the passed string into its individual components
	var nsParts = fullyqualifiednsid.split(".");
	var root = window;

	// For each level of the namespace hierarchy
	for(var i=0; i<nsParts.length; i++)
	{
		// If the next level doesn't exist then create it
		if(typeof(root[nsParts[i]]) == "undefined")
			root[nsParts[i]] = new Object();
			
		// And step down to the next level of the hierarchy
		root = root[nsParts[i]];
	}
	
	return(true);
}


/**
 * namespaceDefined traverses the namespace hierarchy and reports on whether the supplied namespace has already been registered.  Essentially similar to registerNamespace, but doesn't create missing identifiers - instead it returns false when it hits the first part of the fully qualified identifier that hasn't already been defined.
 * @see #registerNamespace 
 * @param {String} fullyqualifiednsid A string of the form "x.y.z", representing the namespace to check.
 * @returns {Boolean} true if identifier exists, false if not. 
 */
function namespaceDefined(fullyqualifiednsid)
{
	var nsParts = fullyqualifiednsid.split(".");
	var root = window;

	for(var i=0; i<nsParts.length; i++)
	{
		if(typeof(root[nsParts[i]]) == "undefined")
			return(false);
		root = root[nsParts[i]];
	}
	
	return(true);
}


/**
 * namespaceDefined traverses the namespace hierarchy and reports on whether the supplied namespace has already been registered.  Essentially similar to registerNamespace, but doesn't create missing identifiers - instead it returns false when it hits the first part of the fully qualified identifier that hasn't already been defined.
 * @see #registerNamespace 
 * @param {String} fullyqualifiednsid A string of the form "x.y.z", representing the namespace to check.
 * @returns {Boolean} true if identifier exists, false if not. 
 */
function requireNamespace(fullyqualifiednsid, sourceoferror)
{
	if(!namespaceDefined(fullyqualifiednsid))
		throw new Phoenix.Exception.MissingLibraryException(fullyqualifiednsid, sourceoferror);

	return(true);
}


/*
	namespaceIdToObjectRef()
	
	Basically the same code as namespaceDefined(), but on instead of returning false/true it returns null/a reference to the namespace object (important: NOT to the HTML Element associated with a control, but to the namespace object that represents that control itself).
*/

/**
 * namespaceIdToObjectRef accepts a namespace identifier as a string and (if it exists) returns a reference to the object which it represents. 
 * @param {String} fullyqualifiednsid A string of the form "x.y.z", representing the namespace object to retrieve.
 * @returns JS object representing identifier if it exists, otherwise false.
 */
function namespaceIdToObjectRef(fullyqualifiednsid)
{
	var nsParts = fullyqualifiednsid.split(".");
	var root = window;

	for(var i=0; i<nsParts.length; i++)
	{
		if(typeof(root[nsParts[i]]) == "undefined")
			return(null);
		root = root[nsParts[i]];
	}
	
	return(root);
}


/*
	registerThisControl()
	
	1. Register the control's namespace identifier, so its presence will be discoverable by other controls/general javascript on any page it's included in.
	
	2. Attach a reference to the control's top-level HTML element to the newly-created object, so we can programmatically manipulate it from Javascript if desired.
*/

function registerThisControl(fullyqualifiedidentifier, rootelementid)
{
	// Given the desired fully-qualified namespace identifier as a text string ("x.y.z"), register this control's namespace object (along with any necessary parent identifiers)
	registerNamespace(fullyqualifiedidentifier);
	
	// Now we need a reference to the control's newly-created namespace *object* ("z", in the example above)
	var nsParts = fullyqualifiedidentifier.split(".");
	var root = window;
	for(var i=0; i<nsParts.length; i++)
		root = root[nsParts[i]];
	
	// Now, given the passed-in HTML ID string we need a reference to the HTML element object it represents.
	var elementobj = document.getElementById(rootelementid);
	if(!elementobj)
		return(false);
	
	// Finally we assign this HTML object reference to the rootElement property of the control object (z.rootElement, in the above example)
	root.rootElement = elementobj;
	
	return(true);
}





/*
	getLeafIdentifiers()
	
	Given a fully-qualified namespace identifier, grab and return all the controls that exist on the page that are of this type.
	
	Return value is an array of hashes.  Each hash comtains the control's namespace identifier (a string), the id of its root HTML element (a string) and a reference to the javascript object in the namespace which represents this particular control (a javascript reference).
		
	Important: the "name" value is not fully-qualified but is the most precise *individual* identifier we can class the control as.
	
	For example, if we have a control in the hierarchy of type Phoenix.Controls.TabStrip.SiteSections, and the argument passed to the function is "Phoenix.Controls", then this control's name will be identified as "SiteSections", not "TabStrip" or "TabStrip.SiteSections".   
*/

function getLeafIdentifiers(fullyqualifiedidentifier)
{
	var thisobject = namespaceIdToObjectRef(fullyqualifiedidentifier);

	if(typeof(thisobject.rootElement) != "undefined")	// "Leaf" element in the namespace - going any further will take us into the controls's containing HTML element (and then into their child elements, and so on)
	{
		var control = new Array();
		control["name"] = fullyqualifiedidentifier.substr(fullyqualifiedidentifier.lastIndexOf(".")+1);
		control["objRef"] = thisobject;
		control["id"] = thisobject.rootElement.id;
		
		var controlsarray = new Array();
		controlsarray[0] = control;
		
		return(controlsarray);
	}
	
	var leafnodes = new Array();
	for(var subobject in thisobject)
	{
		var childcontrols = getLeafIdentifiers(fullyqualifiedidentifier+"."+subobject);

		// Of course, IE doesn't support modern javascript versions (sigh). 
		//for each(var control in childcontrols)
		//	leafnodes[leafnodes.length] = control;
		
		for(var i=0; i<childcontrols.length; i++)
			leafnodes[leafnodes.length] = childcontrols[i]; 
	}
	
	return(leafnodes);
}





// Register top-level namespace and make Exceptions element available so the rest of the Phoenix.* library can complain about missing dependencies
 
registerNamespace("Phoenix");

Phoenix.Exception = function (message, source)
{
	if(typeof(source) == "object" && source.nsIdentifier)
		this.source = source.nsIdentifier;
	else
		this.source = source || "";
		
	this.name = "Phoenix.Exception";
	this.message = message || "Generic Phoenix Exception";
	
	this.toString = function ()
	{
		return("" + this.name + " (" + this.source + "): " + this.message);
	};
};


Phoenix.Exception.MissingLibraryException = function(message, source)
{
	this.base = Phoenix.Exception;
	this.base(message, source);
	this.name = "Phoenix.Exception.MissingLibraryException";
	
	this.toString = function ()
	{
		return("" + this.name + " (" + this.source + "): This method requires access to the library " + this.message + ".\n\nPlease include the relevant library before calling this function.");
	};
};

Phoenix.Exception.ReallyBadIdeaException = function(message, source)
{
	this.base = Phoenix.Exception;
	this.base(message, source);
	this.name = "Phoenix.Exception.ReallyBadIdeaException";
	
	this.toString = function ()
	{
		return("" + this.name + " (" + this.source + "): This is a really bad idea.  " + this.message);
	};
};
