(function() {
var L = YAHOO.lang, Dom = YAHOO.util.Dom, Evt = YAHOO.util.Event, U = ZC.Util, _GT = U.GetText;
var oWidget; // oWidget reused for each new class (helps cut code size once minified)


/***************************************************************************
 * Core_Widget_Checkbox
 */
oWidget = ZC.Core.Widget.Create('Checkbox');
oWidget.prototype.CustomSetupEnd = function()
{
	var sCheckedClass = this.GetAttribDefault('CheckedClass', 'checked'), fnChangeHandler;
	if (this._elInput && this.elContainer && sCheckedClass)
	{
		fnChangeHandler = function()
		{
			Dom[this._elInput.checked ? 'addClass' : 'removeClass'](this.elContainer, sCheckedClass);
		}
		Evt.on(this._elInput, 'change', fnChangeHandler, this, true);
		Evt.on(this._elInput, 'click', fnChangeHandler, this, true); // IE doesn't fire change events until blur *sigh*
		fnChangeHandler.call(this);
	}
}
oWidget.prototype.GetValue = function()
{
	if (!this._elInput)
		return undefined;

	return this._elInput.checked;
}
oWidget.prototype.SetValue = function(bValue)
{
	this._elInput.checked = !!bValue;
	this._FireEventHandlers('change');
}
ZC.Core.Widget.Boolean = oWidget; // alias

/***************************************************************************
 * The following methods are shared between Radio and CheckboxGroup widgets
 */
var fnOptionKeysFindElements = function()
{
	var sID = this.aDef.ID || this._WidgetNameToID();

	if (this.IsReadOnly())
	{
		this._elInput = Dom.get(sID);
		return;
	}

	this.elContainer = Dom.get(sID);
	this._elInput = [];

	if (this.aDef.OptionKeys)
	{
		this._elInput = U.Map(this.aDef.OptionKeys, function(sOptKey)
		{
			return Dom.get(sID + sOptKey);
		});
		this._elInput = U.Filter(this._elInput, function(el) { return L.isObject(el); }); // filter out nulls

		if (this._elInput.length == 0)
			this._elInput = null;
	}

	if (!this.elContainer)
		this._FindContainers();
},
fnGetLabelForElement = function(aInputs)
{
	var aLabels, bReturnArray = true;

	if (L.isUndefined(aInputs))
	{
		aInputs = this._elInput;
	}
	else if (!L.isArray(aInputs))
	{
		bReturnArray = false;
		aInputs = [aInputs];
	}
		
	aLabels = U.Map(aInputs, function(elRadio)
	{
		var sInputId = elRadio.id,
			aFindLabels = Dom.getElementsBy(function(el) { return (el.htmlFor == sInputId); }, 'label');

		if (aFindLabels.length == 0)
			return undefined;

		return aFindLabels[0];
	}, this);

	return (bReturnArray) ? aLabels : aLabels[0];
}

/***************************************************************************
 * Core_Widget_Radio
 */
oWidget = ZC.Core.Widget.Create('Radio');
oWidget.prototype._FindElements = fnOptionKeysFindElements;

/**
 * Finds the selected radio input in the radio group.
 * @private
 * @return {Object} reference to the radio INPUT element
 */
oWidget.prototype._GetSelectedInput = function()
{
	for (var i = 0, iMax = this._elInput.length; i < iMax; ++i)
	{
		if (this._elInput[i].checked)
			return this._elInput[i];
	}
	return undefined;
}
/**
 * Finds the label element for the given input, or all labels for all radios in this widget if not specified.
 * @param [{Array/HTMLElement}] optional element(s) to find label for
 * @return {Array/HTMLElement} either an array of elements or a single label element
 */
oWidget.prototype.GetLabelEl = fnGetLabelForElement;

oWidget.prototype.GetValue = function()
{
	if (this.IsReadOnly())
		return this._elInput.value;

	var elSelected = this._GetSelectedInput();
	return elSelected ? elSelected.value : undefined;
}
oWidget.prototype.GetHTMLValue = function()
{
	if (this.IsReadOnly())
		return this._elInput.value;

	var elSelected = this._GetSelectedInput(), elLabel;
	if (!elSelected)
		return undefined;

	elLabel = this.GetLabelForElement(elSelected);
	if (!elLabel)
		return undefined;

	return elLabel.innerHTML;
}
oWidget.prototype.GetTextValue = function()
{
	if (this.IsReadOnly())
		return this._elInput.value;

	var elSelected = this._GetSelectedInput(), elLabel;
	if (!elSelected)
		return undefined;

	elLabel = this.GetLabelForElement(elSelected);
	if (!elLabel)
		return undefined;

	return (elLabel.textContent || elLabel.innerText);
}
oWidget.prototype.SetValue = function(Value)
{
	if (L.isUndefined(Value))
		return this.Clear();

	for (var i = 0, iMax = this._elInput.length; i < iMax; ++i)
	{
		if (this._elInput[i].value == Value)
		{
			this._elInput[i].checked = true;
			this._FireEventHandlers('change');
			return;
		}
	}
}
oWidget.prototype.Clear = function()
{
	if (!L.isUndefined(this.GetValue()))
	{
		Dom.batch(this._elInput, function(el) { el.checked = false; });
		this._FireEventHandlers('change');
	}
}


/***************************************************************************
 * Core_Widget_CheckboxGroup
 */
oWidget = ZC.Core.Widget.Create('CheckboxGroup');
oWidget.prototype._FindElements = fnOptionKeysFindElements;

oWidget.prototype.CustomSetupEnd = function()
{
	var sSelectedClass = this.aDef.SelectedClass || 'checked';
	var fnToggleSelectedClass = function()
	{
		// 'this' is the HTML element the event fires on
		var elAncestor = Dom.getAncestorByTagName(this, 'tr');
		Dom[this.checked ? 'addClass' : 'removeClass'](elAncestor, sSelectedClass);
	}
	Evt.on(this._elInput, 'change', fnToggleSelectedClass);
	if (YAHOO.env.ua.ie)
		Evt.on(this._elInput, 'click', fnToggleSelectedClass);

	/**
	 * Check initial state. The server-side widget adds the class to the tr if
	 * it's set on the server-side, but that doesn't work if the user reloads
	 * the page in their browser.
	 */
	U.ForEach(this._elInput, function(el) { fnToggleSelectedClass.apply(el); });
}

/**
 * Finds the label element for the given input, or all labels for all radios in this widget if not specified.
 * @param [{Array/HTMLElement}] optional element(s) to find label for
 * @return {Array/HTMLElement} either an array of elements or a single label element
 */
oWidget.prototype.GetLabelEl = fnGetLabelForElement;

/**
 * Finds the selected checkboxes input in the checkbox group.
 * @private
 * @return {Array} array of selected checkbox elements
 */
oWidget.prototype._GetSelectedInputs = function()
{
	return U.Filter(this._elInput, function(el) { return el.checked; });
}
oWidget.prototype.GetValue = function()
{
	var elSelected = this._GetSelectedInputs();
	return U.Map(elSelected, function(el) { return el.value; });
}
oWidget.prototype.GetTextValue = function()
{
	var elSelected = this._GetSelectedInputs(), aTextValues;
	if (!elSelected.length)
		return this.aDef.FormattedValueWhenEmpty || '';

	aTextValues = U.Map(this.GetLabelEl(elSelected), function (el) { return (el.textContent || el.innerText); });
	return aTextValues.join("\n");
}
oWidget.prototype.GetHTMLValue = function()
{
	var elSelected = this._GetSelectedInputs(), aHTMLValues;
	if (!elSelected.length)
		return this.aDef.FormattedValueWhenEmpty || '';

	aHTMLValues = U.Map(this.GetLabelEl(elSelected), function (el) { return el.innerHTML; });
	return aHTMLValues.join(this.aDef.HTMLValueSeparator || "<br />");
}
oWidget.prototype.SetValue = function(aValue)
{
	var bChanged = false;
	U.ForEach(this._elInput, function(el)
	{
		if (U.InArray(el.value, aValue))
			if (!el.checked)
			{
				el.checked = true;
				bChanged = true;
			}
		else
			if (el.checked)
			{
				el.checked = false;
				bChanged = true;
			}
	});
	if (bChanged)
		this._FireEventHandlers('change');
}
/**
 * Enable / disable one or more options.
 * @param {mixed} Value Either an option value, or array of option values
 * @param {Boolean} bEnable If true (default), enables this option
 */
oWidget.prototype.EnableOption = function(Value, bEnable)
{
	if (!L.isArray(Value))
		Value = [Value];

	var sAddRemove = bEnable ? 'removeClass' : 'addClass';
	U.ForEach(this._elInput, function(el)
	{
		if (U.InArray(el.value, Value))
		{
			el.disabled = !bEnable;
			var elTR = Dom.getAncestorByTagName(el, 'tr');
			Dom[sAddRemove](elTR, 'disabled');
		}
	});
}

/***************************************************************************
 * Core_Widget_Select
 */

oWidget = ZC.Core.Widget.Create('Select');
// used in LinkedSelect as well (TODO: rewrite LinkedSelect to use Select widgets)
var fnSetupCopyOptionClass = function()
{
	var elSelectedOption, sCurrentOptionClass, fnChangeHandler;
		
	fnChangeHandler = function(elSelect)
	{
		if (elSelect.target)
			elSelect = Evt.getTarget(elSelect);

		if (this.sOldOptionClass)
			Dom.removeClass(elSelect, this.sOldOptionClass);

		var elSelectedOption = elSelect.options[elSelect.selectedIndex],
			sNewClass = Dom.getAttribute(elSelectedOption, 'class');

		if (sNewClass)
		{
			Dom.addClass(elSelect, sNewClass);
			this.sOldOptionClass = sNewClass;
		}
	}

	if (this.aDef.CopyOptionClass)
	{
		Evt.on(this._elInput, 'change', fnChangeHandler, this, true);
		Evt.on(this._elInput, 'keydown', function() { var self = this; this.iKeyInterval = window.setInterval(function() { fnChangeHandler.apply(self, self._elInput); }, 10) }, this, true);
		Evt.on(this._elInput, 'keyup', function() { window.clearInterval(this.iKeyInterval); }, this, true);
		Dom.batch(this._elInput, fnChangeHandler, this, true);
	}
}
oWidget.prototype.CustomSetupEnd = fnSetupCopyOptionClass;

oWidget.prototype.GetValue = function(bTextValue)
{
	// lazy binding of the required GetValue implementation
	if (this._elInput.type == 'hidden' && this._elInput.tagName.toLowerCase() == 'input')
	{
		// hidden field (only one option), use parent method
		this.bHiddenField = true;
		this.GetValue = oWidget.superclass.GetValue;
	}
	else if (this._elInput.type == 'select-one')
	{
		this.GetValue = function(bTextValue)
		{
			var iIndex = this._elInput.selectedIndex;
			if (iIndex < 0)
				return undefined;

			var oOpt = this._elInput.options[iIndex];
			return bTextValue ? oOpt.text : oOpt.value;
		}
	}
	else
	{
		this.GetValue = function(bTextValue)
		{
			var aValues = [];

			U.ForEach(this._elInput.options, function(oOpt)
		   	{
				if (oOpt.selected) aValues.push(bTextValue ? oOpt.text : oOpt.value);
			});
			return aValues;
		}
	}

	return this.GetValue(bTextValue);
}
oWidget.prototype.GetTextValue = function()
{
	return this._GetFormattedValue("\n");
}
oWidget.prototype.GetHTMLValue = function()
{
	return this._GetFormattedValue(this.aDef.HTMLValueSeparator || '<br />');
}
oWidget.prototype._GetFormattedValue = function(sSeparator)
{
	if (this.bHiddenField)
		return (this.elContainer.textContent || this.elContainer.innerText);

	var Value = this.GetValue(true);

	if (Value.length == 0)
		return (this.aDef.FormattedValueWhenEmpty || '');

	if (!L.isArray(Value))
		return Value;

	return Value.join(sSeparator);
}
oWidget.prototype.SetValue = function(Value)
{
	var bSingle = !L.isArray(Value),
	 	bChanged = false;

	if (this._elInput.type == 'hidden' && this._elInput.tagName.toLowerCase() == 'input')
	{
		return oWidget.superclass.SetValue(Value);
	}

	for (var iOpt = 0, iOptMax = this._elInput.length; iOpt < iOptMax; ++iOpt)
	{
		var oOpt = this._elInput.options[iOpt];
		if (bSingle)
		{
			if (oOpt.value == Value)
			{
				if (!oOpt.selected)
				{
					oOpt.selected = true;
					this._FireEventHandlers('change');
				}
				return;
			}
		}
		else
		{
			var bInArray = U.InArray(oOpt.value, Value);
			bChanged = bChanged || (oOpt.selected != bInArray);
			oOpt.selected = bInArray;
		}
	}
	if (bChanged)
		this._FireEventHandlers('change');
}
oWidget.prototype.SetAttrib = function(sName, AttribValue)
{
	if (sName == 'Options')
	{
		var aSelectedOptions = (this._elInput.type == 'select-one') ? [this.GetValue()] : this.GetValue();
		this._elInput.options.length = 0;

		for (var sOptVal in AttribValue)
		{
			if (L.hasOwnProperty(AttribValue, sOptVal))
			{
				var elOption = document.createElement('option');
				elOption.value = String(sOptVal);
				elOption.innerHTML = String(AttribValue[sOptVal]);
				elOption.selected = U.InArray(sOptVal, aSelectedOptions);

				this._elInput.appendChild(elOption);
			}
		}
	}
	else
		oWidget.superclass.SetAttrib.call(this, sName, AttribValue);
}

oWidget.prototype.Clear = function()
{
	var sFirstOptVal = this._elInput.options[0].value;
	if (sFirstOptVal.match(/^(_?_?Any_?_?|)$/i))
		this._elInput.selectedIndex = 0;
}

ZC.Core.Widget.Create('Reference', 'Core', oWidget);

/***************************************************************************
 * Core_Widget_Button_Clear
 */
oWidget = ZC.Core.Widget.Create('Button_Clear');
oWidget.prototype.CustomSetupStart = function()
{
	if (!this._elInput)
		return;
	var elButton = document.createElement('input');

	var oAttribs = this._elInput.attributes;
	// copy attribs from placeholder tag
	for (var i = 0, iMax = oAttribs.length; i < iMax; i++)
	{
		Dom.setAttribute(elButton, oAttribs[i].nodeName, oAttribs[i].nodeValue);
	}
	elButton.type = 'button';
	elButton.value = this.aDef.Caption || 'Clear';
	var fnButtonClickHandler = function(event)
	{
		this.oForm.Clear();
		Evt.stopEvent(event);
	}

	Evt.on(elButton, 'click', fnButtonClickHandler, this, true);

	this._elInput.parentNode.replaceChild(elButton, this._elInput);
	this._elInput = elButton;
}

/***************************************************************************
 * Core_Widget_Text
 */
oWidget = ZC.Core.Widget.Create('Text');
oWidget.prototype.InsertText = function(sText)
{
	U.InsertAtCursor(this._elInput, sText);
}

/***************************************************************************
 * Core_Widget_TextArea
 */
oWidget = ZC.Core.Widget.Create('TextArea');
oWidget.prototype.CustomSetupEnd = function()
{
	if (this.aDef.Size)
	{
		this.AddEvent(this.UpdateNRemaining, ['keyup', 'focus'],  this);
		this.AddEvent(this.HideNRemaining, 'blur',  this);
	}
}
oWidget.prototype.UpdateNRemaining = function(event)
{
	if (L.isUndefined(this._elRemaining))
	{
		this._elRemaining = document.createElement('span');
		this._elRemaining.className = 'textarea-remainingchars';
		Dom.insertAfter(this._elRemaining, this._elInput);
	}
	var iUsed = this.GetValue().length, iMax = this.aDef.Size, sRemainingText = '';
	var iRemaining = iMax - iUsed;

	if(iRemaining >= 0)
		sRemainingText = U.sprintf('Used %1$d of %2$d characters, %3$d remaining.', iUsed, iMax, iRemaining);
	else
		sRemainingText = U.sprintf('Used %1$d of %2$d characters.', iUsed, iMax);
	
	this._elRemaining.innerHTML = sRemainingText;
	var bOverLimit = iUsed > iMax;
	var sNewClass = bOverLimit ? 'invalid' : 'valid';
	var sOldClass = bOverLimit ? 'valid' : 'invalid';
	Dom.replaceClass(this._elRemaining, sOldClass, sNewClass);

	Dom.removeClass(this._elRemaining, 'invisible');
}
oWidget.prototype.HideNRemaining = function(event)
{
	if (this._elRemaining)
		Dom.addClass(this._elRemaining, 'invisible');
}

oWidget.prototype.Validate = function()
{
	// Enforce the max length
	var iUsed = this.GetValue().length, iMax = this.aDef.Size;
	var sValMsg = sprintf(_GT('Please reduce this to %d  characters, it is currently at %d.'), iMax, iUsed);
	
	if (iUsed > iMax)
	{
		this.SetValid(false, sValMsg);
		return false;
	}
	return oWidget.superclass.Validate.call(this);
}

oWidget.prototype.InsertText = function(sText)
{
	U.InsertAtCursor(this._elInput, sText);
}

/***************************************************************************
 * Core_Widget_HTML
 */
oWidget = ZC.Core.Widget.Create('HTML');
oWidget.prototype.CustomSetupEnd = function()
{
	var iAttempts = 0,
		fnInitMCE = function()
		{
			var oTinyMCE = this.GetTinyMCE(), oWidget = this, fnOldHandleEvent;
			if (!oTinyMCE)
			{
				if (++iAttempts < 10)
				{
					window.setTimeout(function() { fnInitMCE.call(oWidget); }, 200);
				}
				return;
			}
			fnOldHandleEvent = oTinyMCE.settings.handle_event_callback;
			oTinyMCE.settings.handle_event_callback = function(e)
			{
				if (e.type == 'blur')
					oWidget.oSelectionBookmark = oTinyMCE.selection.getBookmark();

				if (fnOldHandleEvent != '' && !L.isUndefined(fnOldHandleEvent)) 
					return tinyMCE.evalFunc(typeof(fnOldHandleEvent) == "function" ? fnOldHandleEvent : eval(fnOldHandleEvent), 0, arguments);

				return true;
			}
		};
	
	Evt.on(window, 'load', fnInitMCE, this, true);
}

oWidget.prototype.GetTinyMCE = function()
{
	if (this._elInput && !this.oTinyMCE)
		this.oTinyMCE = tinyMCE.getInstanceById(this._elInput.id);
	return this.oTinyMCE;
}

oWidget.prototype.GetValue = function()
{
	if (this.GetTinyMCE())
		return this.oTinyMCE.getBody().innerHTML; // later tinyMCE versions have getContent method which runs the cleanup methods on it first
	else
		return oWidget.superclass.GetValue.apply(this, arguments);
}
oWidget.prototype.SetValue = function(sValue)
{
	if (this.GetTinyMCE())
		return this.oTinyMCE.getBody().innerHTML = sValue;  // later tinyMCE versions have setContent method which runs the cleanup methods on it first
	else
		return oWidget.superclass.SetValue.apply(this, arguments);
}
oWidget.prototype.InsertText = function(sText)
{
	// convert common entities, and nl2br
	sText = sText.replace(/['"&<>\n]/g, function(sVal)
	{
		switch (sVal)
		{
			case "\n": return '<br>';
			case "\"": return '&quot;';
			case "'":  return '&apos;';
			case "&":  return '&amp;';
			case "<":  return '&lt;';
			case ">":  return '&gt;';
		}
	});
	this.InsertHTML(sText);
}
oWidget.prototype.InsertHTML = function(sText)
{
	if (this.GetTinyMCE())
	{
		if (this.oSelectionBookmark)
		{
			this.oTinyMCE.selection.moveToBookmark(this.oSelectionBookmark);
		}
		this.oTinyMCE.execCommand('mceInsertContent', false, sText);
	}
	else
	{
		U.InsertAtCursor(this._elInput, sText);
	}
}

/***************************************************************************
 * Core_Widget_Group
 */
oWidget = ZC.Core.Widget.Create('Group');
oWidget.prototype.CustomSetupEnd = function()
{
	if (this.aDef.HideFields)
	{
		var sID, elPlaceHolder, fnToggleVisible, elOpenCloseLink, sLabelShow, sLabelHide, sShowLinkTitle, sHideLinkTitle;

		sLabelShow = this.GetAttribDefault('LabelShow', _GT('Show'));
		sLabelHide = this.GetAttribDefault('LabelHide', _GT('Hide'));
		sShowLinkTooltip = this.GetAttribDefault('ShowLinkTooltip', _GT('Show this section')); 
		sHideLinkTooltip = this.GetAttribDefault('HideLinkTooltip', _GT('Hide this section')); 

		sID = this.aDef.ID;
		elPlaceholder = Dom.get(sID + '-toggle');
		this.elContainer = Dom.get(sID + '-hidediv');

		if (!elPlaceholder)
			return;

		elOpenCloseLink = this.elOpenCloseLink = document.createElement('a');
		elOpenCloseLink.href = '#';
		elOpenCloseLink.innerHTML = sLabelShow;
		this.oOpenCloseTooltip = new YAHOO.widget.Tooltip('grp-oc-tt-' + this.sName, {
			context: elOpenCloseLink, autofillheight: false, autodismissdelay: 120000,
			effect:  { effect: YAHOO.widget.ContainerEffect.FADE, duration: 0.25 },
			text: sShowLinkTooltip
		});
		elPlaceholder.appendChild(elOpenCloseLink);

		var fnToggleVisible = function(event)
		{
			if (event)
				Evt.stopEvent(event);

			if (this.IsVisible())
			{
				this.Hide();
				this.elOpenCloseLink.innerHTML = sLabelShow;
				this.oOpenCloseTooltip.cfg.setProperty('text', sShowLinkTooltip);
			}
			else
			{
				this.Show();
				this.elOpenCloseLink.innerHTML = sLabelHide;
				this.oOpenCloseTooltip.cfg.setProperty('text', sHideLinkTooltip);
			}
		}

		Evt.on(elOpenCloseLink, 'click', fnToggleVisible, this, true);
		if (this.IsVisible())
			fnToggleVisible.call(this);
	}
}

oWidget.prototype.GetTextValue = function()
{
	var aResult = U.Map(this.aChildWidgets, function(oWidget) { return oWidget.GetTextValue(); });
	return aResult.join(this.aDef.TextValueSeparator || ", ");
}

oWidget.prototype.GetHTMLValue = function()
{
	var aResult = U.Map(this.aChildWidgets, function(oWidget) { return oWidget.GetHTMLValue(); });
	return aResult.join(this.aDef.HTMLValueSeparator || "<br />");
}

oWidget.prototype.GetValue = function()
{
	var oValue = {};
	U.ForEach(this.aChildWidgets, function(oWidget, sWidgetName) { oValue[sWidgetName] = oWidget.GetValue(); });
	return oValue;
}

oWidget.prototype.Enable = function(bEnable, sEnableClass, sDisableClass)
{
	U.ForEach(this.aChildWidgets, function(oChild) { oChild.Enable(bEnable, sEnableClass, sDisableClass); });
}
ZC.Core.Widget.Create('Document', 'Core', oWidget);
ZC.Core.Widget.Create('Group_List', 'Core', oWidget);

/***************************************************************************
 * Core_Widget_TextInsertion
 */
oWidget = ZC.Core.Widget.Create('TextInsertion', 'Core', oWidget);
oWidget.prototype.CustomSetupEnd = function()
{
	ZC.JSManager.GetEvent('ManagerInit').subscribe(function()
	{
		var sDestination, fnInsertClick;

		sDestination = this.GetAttrib('Destination');
		// first try this widget's parent, then this widget's form, then finally search the whole page
		this.oDestWidget = this.oParent.GetWidget(sDestination) 
						|| ((this.oParent != this.oForm) && this.oForm.GetWidget(sDestination))
						|| ZC.JSManager.GetWidget(sDestination);

		if (!this.oDestWidget)
			return;

		var fnInsertClick = function()
		{
			this.oDestWidget.InsertText(decodeURIComponent(this.GetWidget('SelectTag').GetValue()));
		}

		this.GetWidget('Insert').AddEvent(fnInsertClick, 'click', this);
		this.GetWidget('SelectTag').AddEvent(fnInsertClick, 'dblclick', this);
	}, this, true);
}


/***************************************************************************
 * Core_Widget_Submit
 */
oWidget = ZC.Core.Widget.Create('Submit');
oWidget.prototype.CustomSetupEnd = function()
{
	var oLoadingPanel, sLoadingMessage;

	if (!this._elInput)
		return;

	if (this.GetAttribDefault('CreateYUIButton'))
	{
		this.oYUIButton = new YAHOO.widget.Button(this._elInput);
		this.oYUIButton.on('click', function() { this._FireEventHandlers('click'); }, this, true);
	}

	if (sLoadingMessage = this.GetAttribDefault('LoadingMessage')) // assignment
	{
		// based on a YUI example - displays a modal "loading" dialog when the submit button is clicked
		oLoadingPanel = new YAHOO.widget.Panel(Dom.generateId(this._elInput) + "wait", {
				fixedcenter:true,  
				close:false,  
				draggable:false,
				zindex:40000, 
				modal:true, 
				visible:false 
			}  
		); 
  
		oLoadingPanel.setHeader(sLoadingMessage); 
		oLoadingPanel.setBody('<img style="margin: 0 auto; display: block;" width="220" height="19" src="/zc/images/ajax-loader-bar.gif">'); 
		oLoadingPanel.render(document.body);

		Evt.on(this._elInput, 'click', function() { oLoadingPanel.show(); });
	}

}

oWidget.prototype.SetAttrib = function(sName, AttribValue)
{
	if (sName == 'value')
	{
		this._elInput.value = AttribValue;
		if (this.oYUIButton)
			this.oYUIButton.set('label', AttribValue);
	}
	else
		oWidget.superclass.SetAttrib.call(this, sName, AttribValue);
}

/***************************************************************************
 * Core_Widget_Button
 */
oWidget = ZC.Core.Widget.Create('Button');
oWidget.prototype.CustomSetupEnd = function()
{
	if (!this._elInput)
		return;

	if (this.GetAttribDefault('CreateYUIButton'))
	{
		this.oYUIButton = new YAHOO.widget.Button(this._elInput);
		this.oYUIButton.on('click', function() { this._FireEventHandlers('click'); }, this, true);
	}
}
ZC.Core.Widget.Create('AutoPopulate', 'Core', oWidget);

/***************************************************************************
 * Core_Widget_MenuButton
 */
oWidget = ZC.Core.Widget.Create('MenuButton');
oWidget.prototype.CustomSetupStart = function()
{
	if (this.GetAttribDefault('Widgets', []).length < 2)
		return;

	// get child widget IDs
	var elSelect = Dom.get(this.aDef.Widgets.MenuOptions.ID),
		elSubmit = Dom.get(this.aDef.Widgets.MenuSubmit.ID), elNewSubmit,
		fnMenuItemHandler, fnMakeMenuItems,
		mMenu, aMenuGroupTitles = []; // will either be a reference to the select widget or an array

	if (!elSelect || !elSubmit)
		return;

	var sType, sTitle = elSubmit.title;
	if (!this.AttribIsset('Default'))
	{
		sType = "menu";
		for (var i = 0, iMax = elSelect.options.length; i < iMax; i++)
		{
			if (elSelect.options[i].value == '')
			{
				elSelect.removeChild(elSelect.options[i]);
				break;
			}
		}
	}
	else
	{
		sType = "split";
	}

	if (!this.AttribIsset('Options'))
	{
		// simple menu - pass select widget directly
		mMenu = elSelect;
	}
	else
	{
		// change button type to "button" to stop YUI from automatically
		// submitting the form when selecting an item in the menu.
		//
		// note: we have to clone the existing node and change the type before
		// replacing the old node, as IE fails if you just change the type of
		// an existing input.
		elNewSubmit = document.createElement('input');
		elNewSubmit.type = 'button';
		elNewSubmit.id = elSubmit.id;
		elNewSubmit.name = elSubmit.name;
		elNewSubmit.value = elSubmit.value;
		elSubmit.parentNode.replaceChild(elNewSubmit, elSubmit);
		elSubmit = elNewSubmit;

		// menu can't be represented as a simple select widget, generate menu items
		fnMenuItemHandler = function(sType, aArgs, oObj) 
		{
			var bResult = true, 
				oButton = oObj.Widget.oYUIMenuButton,
				oHiddenField;

			// scope is the selected menu item
			elSelect.value = this.value;

			// create a hidden field to let the server know this button was clicked
			oHiddenField = document.createElement('input');
			oHiddenField.type = 'hidden';
			oHiddenField.name = oButton.get('name');
			oHiddenField.value = oButton.get('value');
			oButton.getForm().appendChild(oHiddenField);

			// fire general widget click events
			oObj.Widget._FireEventHandlers('click', this);

			// individual options can have click events too
			if (oObj.Events)
			{
				U.ForEach(oObj.Events, function (sEvent)
				{
					bResult = bResult && ZC.JSManager.GetEvent(sEvent).fire(this, oObj.Widget);
				}, this);
			}
			if (bResult && oObj.Submit)
			{

				oButton.submitForm();
			}

			return bResult;
		}
		fnMakeMenuItems = function(aOptions)
		{
			var aMenu = [];

			U.ForEach(aOptions, function(mOption, mKey)
			{
				var aSubMenu, 
					aItemDef = { value: mKey, onclick: { fn: fnMenuItemHandler, obj: { Widget: this, Submit: true } } };

				if (L.isObject(mOption))
				{
					aItemDef.onclick.obj.Submit = L.isUndefined(mOption.Submit) || mOption.Submit;
					aItemDef.onclick.obj.Events = !L.isUndefined(mOption.Events) ? mOption.Events : false;

					if (!L.isUndefined(mOption.Group))
					{
						// groups are just arrays of menu items
						
						aItemDef = fnMakeMenuItems.call(this, mOption.Options);
						if (L.isString(aItemDef.Group))
						{
							aMenuGroupTitles.push(aItemDef.Group);
						}
						else
						{
							aMenuGroupTitles.push(false);
						}
					}
					else
					{
						aItemDef.text = mOption.Text;

						if (!L.isUndefined(mOption.Options))
						{
							if (!L.isUndefined(mOption.Selectable) && !mOption.Selectable)
							{
								delete aItemDef.onclick;
							}

							aSubMenu = fnMakeMenuItems.call(this, mOption.Options);
							if (aSubMenu.length)
								aItemDef.submenu = { id: Dom.generateId(), itemdata: aSubMenu };
						}
					}
				}
				else
				{
					aItemDef.text = mOption;
				}

				aMenu.push(aItemDef);
			}, this);

			return aMenu;
		}

		Dom.addClass(elSelect, 'hide');
		mMenu = fnMakeMenuItems.call(this, this.GetAttrib('Options'));
	}

	this.oYUIMenuButton = new YAHOO.widget.Button(
        elSubmit,
        {
            type: sType,
            menu: mMenu
        }
    );
	U.ForEach(aMenuGroupTitles, function(sTitle, iIndex) { if (sTitle) this.oYUIMenuButton.setItemGroupTitle(sTitle, iIndex); }, this);

	if (sTitle)
	{
		// currently the only way to set custom titles is to override the constants in the button object
		// TODO: fix YUI so this can be done via setting attribs instead, which feels more correct
		var sDefaultTitleVar = sType.toUpperCase() + "BUTTON_DEFAULT_TITLE";
		var sVisibleTitleVar = sType == "menu" ?  "MENUBUTTON_MENU_VISIBLE_TITLE" : "SPLITBUTTON_OPTION_VISIBLE_TITLE";

		this.oYUIMenuButton[sDefaultTitleVar] = elSubmit.title + " (" + this.oYUIMenuButton[sDefaultTitleVar] + ")";
		this.oYUIMenuButton[sVisibleTitleVar] = elSubmit.title + " (" + this.oYUIMenuButton[sVisibleTitleVar] + ")";
		this.oYUIMenuButton.set('title', this.oYUIMenuButton[sDefaultTitleVar]);
	}

	// don't want to create child widget objects
	this.aDef.Widgets = [];
}

/***************************************************************************
 * Core_Widget_Password(_Change)
 */
oWidget = ZC.Core.Widget.Create('Password');
oWidget.prototype.GetValue = function()
{
	return this._elInput[0].value;
}
oWidget.prototype.GetTextValue = function()
{
	return U.StrRepeat('*', this.GetValue().length);
}

oWidget = ZC.Core.Widget.Create('Password_Change', 'Core', oWidget);
oWidget.prototype._FindElements = function()
{
	var sName;

	sName = this._WidgetNameToID();
	this._elInput = [Dom.get(sName), Dom.get(sName + '_confirm')];
	this._FindContainers();
}
oWidget.prototype.Validate = function()
{
	var sVal1, sVal2;

	sVal1 = this._elInput[0].value;
	sVal2 = this._elInput[1].value;

	if (sVal1 && sVal2 && sVal1 != sVal2)
	{
		this.SetValid(false, _GT('The two passwords do not match'));
		return false;
	}
	return oWidget.superclass.Validate.call(this);
}

/***************************************************************************
 * Core_Widget_LinkedSelect_*
 *
 * TODO: move the code from linkedselect.js and from the HTML into here (or probably Widget/LinkedSelect.js).
 * This code is currently quite basic in order to make the Clear button work.
 *
 * LinkedSelects should probably end up using Select child widgets on the client-side as well.
 */
oWidget = ZC.Core.Widget.Create('LinkedSelect');
oWidget.prototype.CustomSetupEnd = fnSetupCopyOptionClass;
oWidget.prototype._FindElements = function()
{
	var sBaseName, i, elInput;

	sBaseName = this._WidgetNameToID();
	i = 0;

	this._elInput = [];
	do
	{
		elInput = Dom.get(sBaseName + '.' + i);
		if (elInput)
			this._elInput.push(elInput);
		i++;
	}
	while (elInput);

	this._FindContainers();
}

oWidget.prototype.Clear = function()
{
	U.ForEach(this._elInput, function(elSelect)
	{
		if (U.InArray(elSelect.options[0].value, ['Any', '__Any__', '']))
			elSelect.selectedIndex = 0;

		if (L.isFunction(elSelect.onchange))
			elSelect.onchange();
	});
}

oWidget.prototype.GetValue = function()
{
	var mVal;
	if(L.isUndefined(this._elInput) || L.isUndefined(this._elInput[this._elInput.length -1]))
	{
		return undefined;
	}

	mVal = this._elInput[this._elInput.length -1].value;

	if(mVal.lastIndexOf('-') != -1)
	{
		mVal = mVal.substring(mVal.lastIndexOf('-') + 1);
	}
	return mVal;
}
ZC.Core.Widget.Create('LinkedSelect_AllWidgetValues', 'Core', oWidget);
ZC.Core.Widget.Create('LinkedSelect_AllowBlank', 'Core', oWidget);
ZC.Core.Widget.Create('LinkedSelect_LinkedSearch', 'Core', oWidget);
ZC.Core.Widget.Create('LinkedSelect_Multiple', 'Core', oWidget);

})();
