registerNamespace("Phoenix.DOM");


/*
	getElementsByClassName()
	
	Return an array of all elements with the CSS class "searchClass".
	Optional: also which are children of "node"
	Optional: also which are of type "tag"
	
	@param searchclass
	An HTML element
	
	@param cssclass
	A string representing a CSS class
	
	@return
	True if the class is already applied to the element, or we successfully add it
	False if the element does not exist, or does not have a className property associated with it
*/

Phoenix.DOM.getElementsByClassName = function (searchclass, node, tag)
{
	var classelements = new Array();
	
	node = node || document;
	tag = tag || '*';

	var els = node.getElementsByTagName(tag);
	var elsLen = els.length;
	var pattern = new RegExp("(^|\\s)"+searchclass+"(\\s|$)");

	for(i=0, j=0; i<elsLen; i++)
	{
		if(pattern.test(els[i].className))
		{
			classelements[j] = els[i];
			j++;
		}
	}
	return(classelements);
};

// Optional: do we want to use Phoenix.DOM.getElementsByClassName() as a library, or do we want to extend the document object to support it?
if(!document.getElementsByClassName)
{
	document.getElementsByClassName = Phoenix.DOM.getElementsByClassName;
}



/*
	addCSSClass()
	
	Add the passed CSS class name to the passed element, if it doesn't have it already.

	@param element
	An HTML element
	
	@param cssclass
	A string representing a CSS class
	
	@return
	True if the class is already applied to the element, or we successfully add it
	False if the element does not exist, or does not have a className property associated with it
*/

Phoenix.DOM.addCSSClass = function(element, cssclass)
{
	if(!element || element.className == undefined)
		return(false);
		
	var elclass = element.className;
	
	var re = new RegExp("(^|\\s+)"+cssclass+"(\\s+|$)");
	if(elclass.match(re))
		return(true);
		
	element.className = (elclass == "")? cssclass: elclass+" "+cssclass;
	
	return(true);
}



/*
	removeCSSClass()

	Remove the passed CSS class name from the passed element, if it has it.
		
	@param element
	An HTML element
	
	@param cssclass
	A string representing a CSS class
	
	@return
	Always returns true
*/

Phoenix.DOM.removeCSSClass = function(element, cssclass)
{
	if(!element || element.className == undefined)
		return(true);
		
	var elclass = element.className;
	
	var re = new RegExp("(^|(?:.*\\s+))"+cssclass+"((?:\\s+.*)|$)");
	while(elclass.match(re))
	{
		element.className = RegExp.$1 + " " + RegExp.$2;
		return(true);
	}
	
	return(true);
}


/*
	getAbsolutePosition()
	
	Retrieve the absolute coordinates of an element.

	@param element
	A DOM element.
	
	@return
	A hash containing keys 'x' and 'y'.
*/
 
Phoenix.DOM.getAbsolutePosition = function (element)
{
    var r = {x:element.offsetLeft, y:element.offsetTop};
    if (element.offsetParent)
		{
        var tmp = this.getAbsolutePosition(element.offsetParent);
        r.x += tmp.x;
        r.y += tmp.y;
    }
    return(r);
};


/*
	getRelativePosition()

	Retrieve the coordinates of an element relative to the top-left corner of another element (first element does not have to be within - or a child of - the second element)

	@param destelement
	An event object
	
	@param srcelement
	An HTML element 

	@return
	A hash containing keys 'x' and 'y', representing the offset of destelement from srcelement.
*/

Phoenix.DOM.getRelativePosition = function (destelement, srcelement)
{
	event = event || window.event;	// Allow for IE-type deviations
	
	var srccoords = this.getAbsolutePosition(srcelement);
	var destcoords = this.getAbsolutePosition(destelement);
	
	var posx, posy;

	posx = destcoords.x - srccoords.x;
	posy = destcoords.y - srccoords.y;
	
	return({x:posx, y:posy});
};

/*
	getComputedStyle()

	Retrieve the *computed* CSS style of an element.

	@param property
	A CSS property name
	
	@param element
	An HTML element
	
	@param pseudoelement
	An (optional) pseudoelement (eg ":first-line")
	
	@return
	The computed value of the specified element/pseudoelement's specified property
*/


Phoenix.DOM.getComputedStyle = function (property, element, pseudoelement)
{
	if(document.defaultView && document.defaultView.getComputedStyle)
	{
		var styleobj = document.defaultView.getComputedStyle(element, pseudoelement);
		return(styleobj.getPropertyValue(property));
	}
	else if(element.currentStyle)
	{
		return(element.currentStyle.getAttribute(property));
	}
};


/*
	generateUniqueId()

	Generate a new element id that's guaranteed unique in the document

	@param prefix
	An optional prefix for the name (can be used to make the random ids more recognisable)
	
	@param minchars
	Minimum number of characters to generate (default: 5)
	
	@param maxchars
	Maximum number of characters to generate (default: 15)
	
	@return
	A generated string beginning with prefix and containing minchars to maxchars additional random alphabetic characters
*/
Phoenix.DOM.generateUniqueId = function (prefix, minchars, maxchars)
{
	if(minchars > maxchars)
	{
		temp = maxchars;
		maxchars = minchars;
		minchars = temp;
	}
	
	minchars = minchars || 5;
	maxchars = maxchars || 15;
	do
	{
		var randomname = prefix;
		var numletters = (Math.random() * (maxchars - minchars))+minchars;	// Generates names between mindigits and maxdigits letters long
		for(var i=0; i<numletters; i++)
			randomname += String.fromCharCode(97 + Math.round(Math.random() * 25));
	}
	while(document.getElementById(randomname) != null);	// Check in case this name exists (pifflingly small chance, but still...) and if so generate a new one from scratch
		
	return(randomname);
};



Phoenix.DOM.getElementOpacity = function (element)
{
	if(element && element.style)
	{
		if(typeof(element.style.filter) != "undefined")	// Have to try IE first, since although it doesn't understand opacity: in CSS files, it will create a "style.opacity" property, initialise it to the value in the CSS and then flatly ignore it.  Argh!
		{
			var regexp = /alpha\(\s*opacity\s*=\s*(\d+)\s*\)/i;	// Pull out the value from "alpha(opacity=value)" syntax
			var matches = element.style.filter.match(regexp);
			if(matches)
				return(parseFloat(Math.round(matches[1])/100, 10));														// And return it as a 0-1 float (so it's comparable to style.opacity values from *proper* browsers)
		}

		else if(typeof(element.style.opacity) != "undefined")	// Try W3C method
			return(parseFloat(element.style.opacity, 10));

	}
	
	return(null);
}


Phoenix.DOM.setElementOpacity = function (element, value)
{
	if(value > 1)
		value = 1;
	if(value < 0)
		value = 0;
		
	if(element && element.style)
	{
		if(typeof(element.style.opacity) != "undefined")	// Try W3C method
			element.style.opacity = value;

		if(typeof(element.style.filter) != "undefined")		// Fall through to IE method
			Phoenix.DOM.addFilter(element, " progid:DXImageTransform.Microsoft.Alpha(opacity="+Math.round(value*100)+")");

		return(true);
	}
	
	return(false);
}


Phoenix.DOM.addFilter = function(element, filtertext)
{
	if(!element || !element.style)
		return(false);

	// Match filter names & separate filter name from parameters
	var filterregexp = /DXImageTransform\.Microsoft\.([^\.\(]+).*/i;
	
  if(element.style.filter != "")
	{
		// Split filter string up into an array of separate filter declarations
		var chunksarray = element.style.filter.split(" progid:");
		var filtershash = new Object();
		
		// Iterate over array, packing the filter names into a hash table (to remove duplicates)
		for(var i=0; i<chunksarray.length; i++)
		{
			var matches = chunksarray[i].match(filterregexp);
			if(matches)
			{
				filtershash[matches[1]] = matches[0];
			}
		}

		// Now separate the new filter declaration into name+parameters strings
		var matches = filtertext.match(filterregexp);
		// And pack it into the hash (so if a filter of that type already exists it overwrites it)
		if(matches)
			filtershash[matches[1]] = matches[0];

		// Now read back out of the hash and stick the filters back in a string
		element.style.filter = "";
		for(field in filtershash)
			element.style.filter += " progid:" + filtershash[field];
	}
	else
	{
		element.style.filter = filtertext + " ";
	}
	
	return(true);
	
}

Phoenix.DOM.removeFilter = function(element, filtername)
{
	if(!element || !element.style)
		return(false);

	// Match filter names & separate filter name from parameters
	var filterregexp = /DXImageTransform\.Microsoft\.([^\.\(]+).*/i;
	
  if(element.style.filter != "")
	{
		// Split filter string up into an array of separate filter declarations
		var chunksarray = element.style.filter.split(" progid:");
		var filtershash = new Object();
		
		// Iterate over array, packing the filter names into a hash table (to remove duplicates)
		for(var i=0; i<chunksarray.length; i++)
		{
			var matches = chunksarray[i].match(filterregexp);
			if(matches)
			{
				filtershash[matches[1]] = matches[0];
			}
		}

		// Now clear the filter that matches the passed-in filtername
		filtershash[filtername] = null;

		// Now read back out of the hash and stick the filters back in a string
		element.style.filter = "";
		for(field in filtershash)
			if(filtershash[field] != null)
				element.style.filter += " progid:" + filtershash[field];
	}
	else
	{
		element.style.filter = filtertext + " ";
	}
	
	return(true);
	
}