// SpryWidget.js - version 0.16 - Spry Pre-Release 1.7

//

// Copyright (c) 2009. Adobe Systems Incorporated.

// All rights reserved.

//

// Redistribution and use in source and binary forms, with or without

// modification, are permitted provided that the following conditions are met:

//

//   * Redistributions of source code must retain the above copyright notice,

//     this list of conditions and the following disclaimer.

//   * Redistributions in binary form must reproduce the above copyright notice,

//     this list of conditions and the following disclaimer in the documentation

//     and/or other materials provided with the distribution.

//   * Neither the name of Adobe Systems Incorporated nor the names of its

//     contributors may be used to endorse or promote products derived from this

//     software without specific prior written permission.

//

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"

// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE

// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE

// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE

// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR

// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF

// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS

// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN

// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)

// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE

// POSSIBILITY OF SUCH DAMAGE.



(function() { // BeginSpryComponent

	

if (typeof Spry == "undefined" || !Spry.Utils || !Spry.$$)

{

	alert("SpryWidget.js requires SpryDOMUtils.js");

	return;

}



if (!Spry.Widget) Spry.Widget = {};



Spry.Widget.setOptions = function(obj, optionsObj, ignoreUndefinedProps)

{

	if (obj && optionsObj)

	{

		for (var optionName in optionsObj)

		{

			var v = optionsObj[optionName];

			if (!ignoreUndefinedProps || v != undefined)

				obj[optionName] = v;

		}

	}

	return obj;

};



Spry.Widget.onLoadDidFire = false;

Spry.Widget.onLoadQueue = [];



Spry.Widget.addCallbackToOnLoadQueue = function(callbackFunc, context)

{

	if (callbackFunc)

	{

		if (context)

		{

			var cf = callbackFunc;

			callbackFunc = function() { cf.call(context); };

		}



		Spry.Widget.onLoadQueue.push(callbackFunc);

	}

};



Spry.Widget.triggerCallbackAfterOnLoad = function(callbackFunc, context)

{

	if (Spry.Widget.onLoadDidFire)

		callbackFunc.call(context);

	else

		Spry.Widget.addCallbackToOnLoadQueue(callbackFunc, context);

		

};



Spry.Widget.processOnLoadQueue = function()

{

	Spry.Widget.onLoadDidFire = true;

	var q = Spry.Widget.onLoadQueue;

	while (q.length)

		(q.shift())();

};



Spry.Utils.addLoadListener(Spry.Widget.processOnLoadQueue);



Spry.Widget.Base = function()

{

	Spry.Widget.Base.Notifier.call(this);

};



Spry.Widget.Base.Notifier = function()

{

	this.observers = [];

	this.suppressNotifications = 0;

};



Spry.Widget.Base.Notifier.prototype.addObserver = function(observer)

{

	if (!observer)

		return;



	// Make sure the observer isn't already on the list.



	var len = this.observers.length;

	for (var i = 0; i < len; i++)

	{

		if (this.observers[i] == observer)

			return;

	}

	this.observers[len] = observer;

};



Spry.Widget.Base.Notifier.prototype.removeObserver = function(observer)

{

	if (!observer)

		return;



	for (var i = 0; i < this.observers.length; i++)

	{

		if (this.observers[i] == observer)

		{

			this.observers.splice(i, 1);

			break;

		}

	}

};



Spry.Widget.Base.Notifier.prototype.notifyObservers = function(methodName, data)

{

	if (!methodName)

		return;



	if (!this.suppressNotifications)

	{

		var len = this.observers.length;

		for (var i = 0; i < len; i++)

		{

			var obs = this.observers[i];

			if (obs)

			{

				if (typeof obs == "function")

					obs(methodName, this, data);

				else if (obs[methodName])

					obs[methodName](this, data);

			}

		}

	}

};



Spry.Widget.Base.Notifier.prototype.enableNotifications = function()

{

	if (--this.suppressNotifications < 0)

	{

		this.suppressNotifications = 0;

		Spry.Debug.reportError("Unbalanced enableNotifications() call!\n");

	}

};



Spry.Widget.Base.Notifier.prototype.disableNotifications = function()

{

	++this.suppressNotifications;

};



Spry.Widget.Base.prototype = new Spry.Widget.Base.Notifier();

Spry.Widget.Base.prototype.constructor = Spry.Widget.Base;



Spry.Widget.Base.getElement = function(ele)

{

	return Spry.$(ele);

};



Spry.Widget.Base.getElements = function(elements)

{

	var eType = typeof elements;

	if (eType == "string")

		return Spry.$$(elements);

	else if (eType == "object")

	{

		if (elements.constructor == Array)

		{

			var result = [];

			for (var i = 0; i < elements.length; i++)

				result = result.concat(Spry.Widget.Base.getElements(elements[i]));

			return result;

		}

		else

			return [elements];

	}



	return [];

};



Spry.Widget.Base.getElementsByClassName = function(root, className)

{

	var results = [];



	if (typeof root.getElementsByClassName != "undefined")

	{

		// Browser has a native getElementsByClassName(), so use it.



		var nodeList = root.getElementsByClassName(className);

		for (var i = 0; i < nodeList.length; i++)

			results.push(nodeList.item(i));

	}

	else

	{

		// Browser has no native getElementsByClassName() implementation

		// so do a manual search.



		var re = new RegExp("\\b" + className + "\\b");

		var nodeList = root.getElementsByTagName("*");

		for (var i = 0; i < nodeList.length; i++)

		{

			var ele = nodeList.item(i);

			if (ele.className.search(re) != -1)

				results.push(ele);

		}

	}



	return results;

};



Spry.Widget.Base.prototype.getElementChildren = function(element)

{

	var children = [];

	if (element)

	{

		var child = element.firstChild;

		while (child)

		{

			if (child.nodeType == 1 /* Node.ELEMENT_NODE */)

				children.push(child);

			child = child.nextSibling;

		}

	}

	return children;

};



Spry.Widget.Base.prototype.groupContentByDelimeter = function(delimeterElements)

{

	var results = new Array();



	var numDelims = delimeterElements.length;

	for (var i = 0; i < numDelims; i++)

	{

		var delim = delimeterElements[i];

		var group = new Array();

		group.push(delim);



		var nextDelim = delimeterElements[i+1];

		var sib = delim.nextSibling;

		while (sib && sib != nextDelim)

		{

			group.push(sib);

			sib = sib.nextSibling;

		}

		

		results.push(group);

	}



	return results;

};



Spry.Widget.Base.prototype.createElement = function(elementName, className, parent, child)

{

	var ele = document.createElement(elementName);

	if (className) ele.className = className;

	if (parent) parent.appendChild(ele);

	if (child) ele.appendChild(child);

	return ele;

};



Spry.Widget.Base.prototype.sliceLeftClassStr =   "Left";

Spry.Widget.Base.prototype.sliceRightClassStr =  "Right";

Spry.Widget.Base.prototype.sliceCenterClassStr = "Center";

Spry.Widget.Base.prototype.sliceTopClassStr =    "Top";

Spry.Widget.Base.prototype.sliceBottomClassStr = "Bottom";



Spry.Widget.Base.prototype.sliceFuncs = {};



Spry.Widget.Base.prototype.sliceFuncs["2slice"] = function(root, eleName, baseClassName)

{

	var a = root ? root : document.createElement(eleName);

	var b = document.createElement(eleName);



	this.appendChildNodes(b, this.extractChildNodes(a)); // Transfer any children into the new content container.



	a.appendChild(b);



	this.addClassName(a, baseClassName + this.sliceLeftClassStr);

	b.className = baseClassName + this.sliceRightClassStr;



	a.contentContainer = b;



	return a;

};



Spry.Widget.Base.prototype.sliceFuncs["3slice"] = function(root, eleName, baseClassName)

{

	var a = root ? root : document.createElement(eleName);

	var b = document.createElement(eleName);

	var c = document.createElement(eleName);



	this.appendChildNodes(c, this.extractChildNodes(a)); // Transfer any children into the new content container.



	a.appendChild(b);

	b.appendChild(c);



	this.addClassName(a, baseClassName + this.sliceLeftClassStr);

	b.className = baseClassName + this.sliceRightClassStr;

	c.className = baseClassName + this.sliceCenterClassStr;



	a.contentContainer = c;



	return a;

};



Spry.Widget.Base.prototype.sliceFuncs["3sliceStacked"] = function(root, eleName, baseClassName)

{

	root = root ? root : document.createElement(eleName);



	var l = document.createElement(eleName);

	var m = document.createElement(eleName);

	var r = document.createElement(eleName);



	this.appendChildNodes(m, this.extractChildNodes(root)); // Transfer any children into the new content container.



	root.appendChild(l);

	root.appendChild(m);

	root.appendChild(r);



	this.addClassName(root, baseClassName);

	l.className = baseClassName + this.sliceLeftClassStr;

	m.className = baseClassName + this.sliceCenterClassStr;

	r.className = baseClassName + this.sliceRightClassStr;



	root.contentContainer = m;



	return root;

};



Spry.Widget.Base.prototype.sliceFuncs["9slice"] = function(root, eleName, baseClassName)

{

	if (!root)

		root = document.createElement(eleName);

	this.addClassName(root, baseClassName);



	var t = this.create3SliceStructure(null, eleName, baseClassName + this.sliceTopClassStr);

	var m = this.create3SliceStructure(null, eleName, baseClassName);

	var b = this.create3SliceStructure(null, eleName, baseClassName + this.sliceBottomClassStr);



	this.appendChildNodes(m.contentContainer, this.extractChildNodes(root)); // Transfer any children into the new content container.



	root.appendChild(t);

	root.appendChild(m);

	root.appendChild(b);



	var contentContainer = m.contentContainer;

	root.contentContainer = contentContainer;

	contentContainer.rootContainer = root;



	return root;

};



// XXX: REMOVE THESE AFTER WIDGETS HAVE BEEN CLEANED UP!

Spry.Widget.Base.prototype.create3SliceStructure = Spry.Widget.Base.prototype.sliceFuncs["3slice"];

Spry.Widget.Base.prototype.create9SliceStructure = Spry.Widget.Base.prototype.sliceFuncs["9slice"];

// XXX



Spry.Widget.Base.prototype.createOptionalSlicedStructure = function(root, eleName, className, sliceMap, childEleName)

{

	// root         - null or the dom element that will serve as the root of the sliced structure.

	//                If null, this function will create the root container using the element name specified.

	// eleName      - The tag to use when creating the sliced structure.

	// className    - The class names placed on each element within the sliced structure will be derived from this name.

	// sliceMap     - null or a dictionary of class name keys whose values are either "9slice", "3slice", or "none".

	//                If null, the widget's sliceMap property is used.

	// childEleName - If specified, the eleName arg will only be used for the first element created within the structure. All

	//                other elements will be created with the specified childEleName.



	if (!sliceMap)

		sliceMap = this.sliceMap ? this.sliceMap : {};



	if (!childEleName)

		childEleName = eleName;



	var sliceType = sliceMap[className];

	sliceType = sliceType ? sliceType : "none";



	if (!root)

		root = document.createElement(eleName);

	this.addClassName(root, className);



	var sliceFunc = this.sliceFuncs[sliceType];

	if (sliceFunc)

		root = sliceFunc.call(this, root, childEleName, className);

	else

		root.contentContainer = root;



	return root;

};



Spry.Widget.Base.prototype.extractChildNodes = function(ele)

{

	var children = [];

	while (ele.firstChild)

	{

		var c = ele.firstChild;

		children.push(c);

		ele.removeChild(c);

	}

	return children;

};



Spry.Widget.Base.prototype.appendChildNodes = function(ele, nodes)

{

	for (var i = 0; i < nodes.length; i++)

		ele.appendChild(nodes[i]);

};



Spry.Widget.Base.prototype.setOptions = Spry.Widget.setOptions;

Spry.Widget.Base.prototype.getOnLoadDidFire = function() { return Spry.Widget.onLoadDidFire; };

Spry.Widget.Base.prototype.addCallbackToOnLoadQueue = Spry.Widget.addCallbackToOnLoadQueue;

Spry.Widget.Base.prototype.triggerCallbackAfterOnLoad = Spry.Widget.triggerCallbackAfterOnLoad;



Spry.Widget.Base.prototype.getElement = Spry.Widget.Base.getElement;

Spry.Widget.Base.prototype.getElements = Spry.Widget.Base.getElements;

Spry.Widget.Base.prototype.addClassName = Spry.Utils.addClassName;

Spry.Widget.Base.prototype.hasClassName = Spry.Utils.hasClassName;

Spry.Widget.Base.prototype.removeClassName = Spry.Utils.removeClassName;

Spry.Widget.Base.prototype.addEventListener = Spry.Utils.addEventListener;

Spry.Widget.Base.prototype.removeEventListener = Spry.Utils.removeEventListener;



Spry.Widget.Base.prototype.indexOf = function(a, v)

{

	// IE6 doesn't support indexOf on Arrays so we need to check

	// for built-in support first. If not found manually do the

	// search.

	if (a)

	{

		if (a.indexOf)

			return a.indexOf(v);

		for (var i = 0; i < a.length; i++)

			if (a[i] == v)

				return i;

	}

	return -1;

};



Spry.Widget.Base.prototype.initializePlugIns = function(defaultPlugIns, widgetOpts)

{

	var evt = new Spry.Widget.Event(this);

	this.notifyObservers("onPreInitializePlugIns", evt);

	if (!evt.performDefaultAction)

		return;



	// Both defaultPlugIns and widgetOpts are optional so make sure

	// we have always have something to work with.



	var opts = widgetOpts ? widgetOpts : {};

	var useDefaults = (typeof opts.useDefaultPlugIns == "undefined") ? true : opts.useDefaultPlugIns;



	var dp = (useDefaults && defaultPlugIns) ? defaultPlugIns : [];

	var np = opts.plugIns ? opts.plugIns : [];



	// Build a list of unique plugins from the default and user-specified sets.



	var plugIns = [];

	var plist = dp.concat(np);

	for (var i = 0; i < plist.length; i++)

	{

		var p = plist[i];

		if (this.indexOf(plugIns, p) < 0)

			plugIns.push(p);

	}



	// Sort the resulting set of plugins based on priority.



	plugIns = plugIns.sort(function(a, b)

	{

		var ap = (typeof a.priority == "undefined") ? 50 : a.priority;

		var bp = (typeof b.priority == "undefined") ? 50 : b.priority;

		return ap - bp;

	});



	// Store the sorted list of plugins on the widget.



	this.plugIns = plugIns;



	// Instantiate each plugin.



	for (var i = 0; plugIns && i < plugIns.length; i++)

	{

		if (plugIns[i].initialize)

			plugIns[i].initialize(this);

	}



	this.notifyObservers("onPostInitializePlugIns", evt);

};



Spry.Widget.Base.prototype.getClientPosition = function(ele)

{

	var pos = new Object;

	pos.x = ele.offsetLeft;

	pos.y = ele.offsetTop;

	var parent = ele.offsetParent;

	while (parent)

	{

		pos.x += parent.offsetLeft;

		pos.y += parent.offsetTop;

		parent = parent.offsetParent;

	}

	return pos;

};



Spry.Widget.Base.prototype.getStyleProp = function(element, prop)

{

	var value;

	var camelized = Spry.Utils.camelizeString(prop);

	try

	{

		if (element.style)

			value = element.style[camelized];



		if (!value)

		{

			if (document.defaultView && document.defaultView.getComputedStyle)

			{

				var css = document.defaultView.getComputedStyle(element, null);

				value = css ? css.getPropertyValue(prop) : null;

			}

			else if (element.currentStyle) 

			{

					value = element.currentStyle[camelized];

			}

		}

	}

	catch (e) {}



	return value == 'auto' ? null : value;

};



Spry.Widget.Base.prototype.makePositioned = function(element)

{

	var pos = this.getStyleProp(element, 'position');

	if (!pos || pos == 'static')

	{

		element.style.position = 'relative';



		// Opera returns the offset relative to the positioning context, when an

		// element is position relative but top and left have not been defined

		if (window.opera)

		{

			element.style.top = 0;

			element.style.left = 0;

		}

	}

};



Spry.Widget.Base.prototype.clearIEAlphaFilter = function(ele)

{

	var filter = ele.style.filter;



	// IE uses an alpha() filter for opacity. The filter style

	// property can contain multiple commands, so the idea here

	// is to just strip out the alpha(filter) and append a new

	// one, leaving any other filters untouched.



	if (filter)

	{

		filter = filter.replace(/alpha\([^\)]*\)/, "");

		filter = filter.replace(/^\s+|\s+$/, "");

		ele.style.filter = filter;

	}

	else

		filter = "";



	return filter;

};



Spry.Widget.Base.prototype.setOpacity = function(ele, opacity)

{

	ele.style.opacity = "" + opacity;



	var filter = this.clearIEAlphaFilter(ele);

	if (filter)

		filter += " ";



	ele.style.filter = filter + "alpha(opacity=" + (opacity*100) + ")";

};



Spry.Widget.Event = function(widget, opts)

{

	this.widget = widget;

	Spry.Widget.setOptions(this, opts);

	this.performDefaultAction = true;

};



Spry.Widget.Event.prototype.preventDefault = function() { this.performDefaultAction = false; };



////////////////////////////////////////////////////////



Spry.Widget.Button = function(ele, opts)

{

	Spry.Widget.Base.call(this);



	this.element = Spry.$$(ele)[0];



	// Initialize the button object with the global defaults.



	this.setOptions(this, Spry.Widget.Button.config);

	

	// Override the defaults with any options passed into the constructor.



	this.setOptions(this, opts);



	var self = this;



	this.addEventListener(this.element, "mousedown", function(e) { return self.handleMouseDown(e); }, false);

	this.addEventListener(this.element, "mouseover", function(e) { return self.handleMouseOver(e); }, false);

	this.addEventListener(this.element, "mouseout", function(e) { return self.handleMouseOut(e); }, false);



	// XXX: IE doesn't allow the setting of tabindex dynamically. This means we can't

	// rely on adding the tabindex attribute if it is missing to enable keyboard navigation

	// by default.



	// Find the first element within the tab container that has a tabindex or the first

	// anchor tag.

	this.focusElement = this.getFocusElement(this.element);

	if (this.focusElement)

	{

		this.addEventListener(this.focusElement, "focus", function(e) { return self.handleFocus(e); }, false);

		this.addEventListener(this.focusElement, "blur", function(e) { return self.handleBlur(e); }, false);

		this.addEventListener(this.focusElement, "keydown", function(e) { return self.handleKeyDown(e); }, false);

	}



	// We need to eat the onclick event so that buttons made

	// from links don't follow the link.

	

	this.addEventListener(this.element, "click", function(e) { return false; }, false);



	this.mouseUpCallback = function(evt) { return self.handleMouseUp(evt); };

};



Spry.Widget.Button.config = {

	disabled:             false,

	mouseOutCancelsClick: true,

	onclick:              null,

	downClass:            "ButtonDown",

	hoverClass:           "ButtonHover",

	disabledClass:        "ButtonDisabled",

	focusedClass:         "ButtonFocused"

};





Spry.Widget.Button.prototype = new Spry.Widget.Base();

Spry.Widget.Button.prototype.constructor = Spry.Widget.Button;



Spry.Widget.Button.prototype.handleMouseDown = function(evt)

{

	if (this.disabled)

		return false;



	this.addClassName(this.element, this.downClass);

	this.addEventListener(document, "mouseup", this.mouseUpCallback, true);



	this.notifyObservers("onButtonDown", { event: evt });

};



Spry.Widget.Button.prototype.handleMouseUp = function(evt)

{

	if (this.disabled)

		return false;



	this.removeClassName(this.element, this.downClass);

	this.removeEventListener(document, "mouseup", this.mouseUpCallback, true);



	if (this.onclick)

		this.onclick(evt);



	this.notifyObservers("onButtonUp");

	this.notifyObservers("onButtonClick");

};



Spry.Widget.Button.prototype.handleMouseOver = function(evt)

{

	if (this.disabled)

		return false;



	this.addClassName(this.element, this.hoverClass);

	this.notifyObservers("onButtonEnter");

};



Spry.Widget.Button.prototype.handleMouseOut = function(evt)

{

	if (this.disabled)

		return false;



	var ele = this.element;

	this.removeClassName(ele, this.hoverClass);



	if (this.mouseOutCancelsClick)

	{

		this.removeClassName(ele, this.downClass);

		this.removeEventListener(document, "mouseup", this.mouseUpCallback, true);

	}

	

	this.notifyObservers("onButtonExit");

};



Spry.Widget.Button.prototype.handleFocus = function(evt)

{

	if (this.disabled)

		return false;



	this.addClassName(this.element, this.focusedClass);

	this.notifyObservers("onButtonFocused");

};



Spry.Widget.Button.prototype.handleBlur = function(evt)

{

	if (this.disabled)

		return false;



	this.removeClassName(this.element, this.focusedClass);

	this.notifyObservers("onButtonBlur");

};



Spry.Widget.Button.prototype.handleKeyDown = function(evt)

{

	if (this.disabled)

		return false;

	this.notifyObservers("onButtonKeyDown", {event: evt, element: this.element});

};



Spry.Widget.Button.prototype.getFocusElement = function(element) {

	var focusElement = null;

	var indexEle = null;

	var anchorEle = null;



	this.preorderTraversal(element, function(node) {

		if (node.nodeType == 1 /* NODE.ELEMENT_NODE */)

		{

			var tabIndexAttr = element.attributes.getNamedItem("tabindex");

			if (tabIndexAttr)

			{

				indexEle = node;

				return true;

			}

			if (!anchorEle && node.nodeName.toLowerCase() == "a")

				anchorEle = node;

		}

		return false;

	});



	if (indexEle)

		focusElement = indexEle;

	else if (anchorEle)

		focusElement = anchorEle;

	return focusElement;

};



Spry.Widget.Button.prototype.preorderTraversal = function(root, func)

{

	var stopTraversal = false;

	if (root)

	{

		stopTraversal = func(root);

		if (root.hasChildNodes())

		{

			var child = root.firstChild;

			while (!stopTraversal && child)

			{

				stopTraversal = this.preorderTraversal(child, func);

				try { child = child.nextSibling; } catch (e) { child = null; }

			}

		}

	}

	return stopTraversal;

};



Spry.Widget.Button.prototype.disable = function()

{

	this.disabled = true;

	this.removeClassName(this.element, this.downClass);

	this.removeClassName(this.element, this.hoverClass);

	this.addClassName(this.element, this.disabledClass);

	this.removeEventListener(document, "mouseup", this.mouseUpCallback, true);

};



Spry.Widget.Button.prototype.enable = function()

{

	this.disabled = false;

	this.removeClassName(this.element, this.disabledClass);

};



Spry.Widget.Button.prototype.focus = function()

{

	if (this.disabled)

		return false;



	if (this.focusElement)

		this.focusElement.focus();

};





})(); // EndSpryComponent


