/*______________
|       ______  |   U I Z E     J A V A S C R I P T     A P I
|     /      /  |   -----------------------------------------
|    /    O /   |    MODULE : Uize.Widget.Button Class (version 1.2.2)
|   /    / /    |    AUTHOR : Chris van Rensburg (http://www.tomkidding.com)
|  /    / /  /| |    ONLINE : http://www.tomkidding.com/uize/uize-js-api
| /____/ /__/_| | COPYRIGHT : (c)2004-2006 Chris van Rensburg
|          /___ |   LICENSE : Distributed under the terms of the GNU General Public License
|_______________|             http://www.gnu.org/licenses/gpl.txt
*/

/*
	DESCRIPTION
		Implements a JavaScript class for button widgets

	REQUIRES
		- Uize.Widget.js (base class)
		- Uize.Node.js
		- Uize.Tooltip (only if the tooltip property is used)

	TO DO
		- intercept events for grayed state node (perhaps events node should always be displayed but just have cursor change?)
		- double-click actions for buttons
		- state->node map to allow different naming schemes? (grayed vs. dimmed, etc.)
		- state->className map to allow different naming schemes
		- state cumulative mode (exclusive or cumulative)
*/

/*ScruncherSettings Mappings="=c" LineCompacting="TRUE"*/

(function () {
	/*** Variables for Scruncher Optimization ***/
		var
			_null = null,
			_true = true,
			_false = false,
			_Uize_Node = Uize.Node
		;

	/*** Object Constructor ***/
		var
			_superclass = Uize.Widget,
			_class = _superclass.Button = _superclass.subclass (
				function () {
					this._tooltipShown = _false;
				}
			),
			_classPrototype = _class.prototype
		;

	/*** Global Variables ***/
		var
			_overButton = _null,
			_statePrecedenceMaps = {},
			_stateNamesToBitMasks = {
				grayed:16,
				'':8,
				over:4,
				active:2,
				playing:1
			}
		;

	/*** Private Instance Methods ***/
		_classPrototype._isClickable = function () {
			return (!this._selected || this._clickToDeselect) && this.isEnabled ();
		};

	/*** Public Instance Methods ***/
		_classPrototype.updateUiBusy = function () {
			var _this = this;
			if (_this.wired ()) {
				if (_this.isBusy ()) {
					_Uize_Node.setStyle (_this._eventsNode,{cursor:'wait'});
				} else {
					_Uize_Node.showClickable (_this._eventsNode,_this._isClickable ());
				}
			}
		};

		var _updateUi = _classPrototype.updateUi = _classPrototype._updateUi = function () {
			var _this = this;
			if (_this.wired ()) {
				var
					_isEnabled = _this.isEnabled (),
					_isBusy = _this.isBusy (),
					_stateCombinationNo =
						(!_isEnabled ? 16 : 0) +
							/* grayed state */
						((_this._state == '' || _isBusy) && _isEnabled ? 8 : 0) +
							/* default state */
						(_this == _overButton && _isEnabled && !_isBusy ? 4 : 0) +
							/* over state */
						(((_this._state == 'down' && !_isBusy) || _this._selected) && _isEnabled ? 2 : 0) +
							/* active state */
						(_this._playing && _isEnabled ? 1 : 0)
							/* playing state */
					,
					_firstShownState = _this._statePrecedenceMap [_stateCombinationNo]
				;
				if (typeof _firstShownState != 'string') {
					var _statePrecedence = _this._statePrecedence;
					for (var _stateNo in _statePrecedence) {
						var _stateName = _statePrecedence [_stateNo];
						if (_stateCombinationNo & _stateNamesToBitMasks [_stateName]) {
							_firstShownState = _stateName;
							break;
						}
					}
					_this._statePrecedenceMap [_stateCombinationNo] = _firstShownState;
				}
				if (_this._mode == 'frames') {
					_this._framesNode.style.top = '-' + (_this._frameOrder._frameOrderMap [_firstShownState] * _this._framesParentNodeDims.height) + 'px';
				} else if (_this._mode == 'classes') {
					var _oldClass = _this._simpleNode.className;

					if (typeof _this._buttonClass != 'string') {
						var _buttonClassMatches =
							_oldClass.match (/(?:(?:(\S+)\s+\1(Grayed|Over|Active|Playing)))/g)
							// finds all matches of "className className[STATE]"
						;
						if (_buttonClassMatches) {
							_this._buttonClass = _buttonClassMatches [_buttonClassMatches.length - 1].split (' ') [0];
						} else {
							// find the last class name that doesn't have a state name at the end
							_buttonClassMatches =
								_oldClass.replace (
									/\S+(Grayed|Over|Active|Playing)\b/g,''
								).match (
									/(\S+)\s*$/
								)
							;
							if (_buttonClassMatches)
								_this._buttonClass = _buttonClassMatches [_buttonClassMatches.length - 1]
							;
						}
					}
					if (_this._buttonClass) {
						var
							_newClassStateQualifier = _firstShownState ? _this._buttonClass + _class.capFirstChar (_firstShownState) : '',
							_newClass = _oldClass.replace (
								new RegExp (
									_this._buttonClass + '(\\s+' + _this._buttonClass + '(Grayed|Over|Active|Playing))?'
								),
								_this._buttonClass + (_newClassStateQualifier ? (' ' + _newClassStateQualifier) : '')
							)
						;
						if (_newClass != _oldClass) _this._simpleNode.className = _newClass;
					}
				} else if (_this._mode == 'nodes') {
					function _showButtonState (_stateNodeName,_mustShow) {
						_this.showNode (_stateNodeName,_mustShow && _this._live);
					}
					for (var _stateName in _stateNamesToBitMasks)
						_showButtonState (_stateName,_stateName == _firstShownState)
					;
					_showButtonState ('events',_this._isClickable ());
				} else if (_this._mode == 'src') {
					// not implemented yet
				}
				if (_this._tooltip) {
					var _newTooltipShown = _this._state == 'over' && _isEnabled && !_this._selected;
					if (_newTooltipShown != _this._tooltipShown) {
						_this._tooltipShown = _newTooltipShown;
						Uize.Tooltip.showTooltip (_this._tooltip,_newTooltipShown);
					}
				}
				_this.updateUiBusy ();
			}
		};

		_classPrototype.wireUi = function () {
			var _this = this;
			if (!_this.wired () && _this.get ('idPrefix')) {
				_this._simpleNode = _this.getNode ();
				_this._eventsNode = _this.getNode ('events');
				_this._framesNode = _this.getNode ('frames');

				if (_this._simpleNode || _this._eventsNode || _this._framesNode) {
					if (_this._framesNode) {
						_this._mode = 'frames';
						if (!_this._eventsNode)
							_this._eventsNode = _this._simpleNode
						;
						_this._framesParentNodeDims = _Uize_Node.getDimensions (_this._framesNode.parentNode);
					} else if (_this._simpleNode && !_this._eventsNode) {
						/* IMPORTANT: need a better way of auto-detecting that a button is in classes mode */
						_this._mode = 'classes';
						_this._eventsNode = _this._simpleNode;
					}

					/*** wire up event handlers ***/
						if (_this._followLink && _this._eventsNode.tagName == 'A')
							_this._eventsNode.onclick = _Uize_Node.returnTrue
						;
						function _fireEvent (_eventName,_domEvent,_cancelBubble) {
							var _firedEvent = _this.isEnabled () && !_this.isBusy ();
							if (_firedEvent) {
								if (_cancelBubble) _domEvent.cancelBubble = _true;
								_this.fireEvent ({
									name:_eventName,
									domEvent:_domEvent
								});
							}
							return _firedEvent;
						}
						_this.wireNodeEvents (
							_this._eventsNode,
							{
								onmouseover:function (_domEvent) {
									_this.set ({_state:'over'});
									_fireEvent ('Over',_domEvent);
									return _false;
								},
								onmouseout:function (_domEvent) {
									_this.set ({_state:''});
									_fireEvent ('Out',_domEvent);
									return _false;
								},
								onmousedown:function (_domEvent) {
									_this.set ({_state:'down'});
									_fireEvent ('Down',_domEvent);
									return _false;
								},
								onmouseup:function (_domEvent) {
									_this.set ({_state:'over'});
									_fireEvent ('Up',_domEvent);
									return _false;
								},
								onclick:function (_domEvent) {
									/*
									if (_this.isEnabled () && !_this.isBusy ()) {
										if (_this._selected && _this._clickToDeselect) _this.set ({_selected:_false});
									}
									*/
									return _fireEvent ('Click',_domEvent,_true) && _this._followLink;
								}
							}
						);

					_superclass.prototype.wireUi.call (_this);
				}
			}
		};

	/*** Public Static Methods ***/
		_class.addChildButton = function (_buttonId,_clickHandler,_isLinkButton) {
			var
				_this = this,
				_button
			;
			function _makeButton () {
				_button = new _class ();
				_button.addEventHandler (
					'Click',
					function (_event) {
						_event.buttonId = _buttonId;
						if (typeof _clickHandler == 'function') _clickHandler (_event);
						_this.fireEvent (_event);
					}
				);
			}
			if (_this == _class) {
				/*** being used as a static method ***/
					_makeButton ();
					_button.set ({
						idPrefix:_buttonId,
						_followLink:_true
					});
					window [_button.objectName] = _button;
					_button.wireUi ();
			} else {
				/*** being used as an instance method, stitched in on some other widget class ***/
					_button = _this.widgets.map [_buttonId];
					if (!_button) {
						_makeButton ();
						_this.widgets.add (_buttonId,_button);
					}
			}
			return _button;
		};

		_class.addLinkButton = function (_buttonId) {
			return this.addChildButton (_buttonId,_null,_true);
		};

	/*** Setup Properties ***/
		_class.registerProperties ({
			_clickToDeselect:{
				name:'clickToDeselect',
				onChange:_updateUi,
				value:_false
			},
			_frameOrder:{
				name:'frameOrder',
				onChange:function () {
					var _frameOrder = this._frameOrder;
					if (!_frameOrder._frameOrderMap) {
						var _map = _frameOrder._frameOrderMap = {};
						for (var _frameNo = 0; _frameNo < _frameOrder.length; _frameNo++)
							_map [_frameOrder [_frameNo]] = _frameNo
						;
					}
				},
				value:['grayed','','over','active','playing']
			},
			_followLink:{
				name:'followLink',
				value:_false
			},
			_live:{
				name:'live',
				onChange:_updateUi,
				value:_true
			},
			_mode:{
				name:'mode',
				value:'nodes'
			},
			_playing:{
				name:'playing',
				onChange:_updateUi,
				value:_false
			},
			_selected:{
				name:'selected',
				onChange:_updateUi,
				value:_false
			},
			_state:{
				name:'state',
				onChange:function () {
					var _this = this;
					if (!_this._state) {
						if (_overButton == _this)
							_overButton = _null
						;
					} else if (_this._state == 'over') {
						if (_overButton && _overButton != _this)
							_overButton.set ({_state:''})
						;
						_overButton = _this;
					}
					_this._updateUi ();
				},
				value:''
			},
			_statePrecedence:{
				name:'statePrecedence',
				onChange:function () {
					var
						_this = this,
						_statePrecedenceAsJoinedStr = _this._statePrecedence._asJoinedStr
					;
					if (!_statePrecedenceAsJoinedStr)
						_statePrecedenceAsJoinedStr = _this._statePrecedence._asJoinedStr = _this._statePrecedence.join (',')
					;
					var _statePrecedenceMap = _statePrecedenceMaps [_statePrecedenceAsJoinedStr];
					if (!_statePrecedenceMap)
						_statePrecedenceMap = _statePrecedenceMaps [_statePrecedenceAsJoinedStr] = {}
					;
					_this._statePrecedenceMap = _statePrecedenceMap;
					_this.updateUi ();
				},
				value:['grayed','playing','active','over','']
			},
			_tooltip:'tooltip'
		});
}) ();

