DIR : /home/kozerus/public_html/pn/uize/site-source/js/Uize/Widget/FormElement.js

/home/kozerus/public_html/pn/uize/site-source/js/Uize/Widget

/*______________
|       ______  |   U I Z E    J A V A S C R I P T    F R A M E W O R K
|     /      /  |   ---------------------------------------------------
|    /    O /   |    MODULE : Uize.Widget.FormElement Class
|   /    / /    |
|  /    / /  /| |    ONLINE : http://www.uize.com
| /____/ /__/_| | COPYRIGHT : (c)2007-2016 UIZE
|          /___ |   LICENSE : Available under MIT License or GNU General Public License
|_______________|             http://www.uize.com/license.html
*/

/* Module Meta Data
	type: Class
	importance: 7
	codeCompleteness: 80
	docCompleteness: 50
*/

/*?
	Introduction
		The =Uize.Widget.FormElement= class serves as a wrapper class in order to provide an interface for any form element (input, select, textarea, etc).

		*DEVELOPERS:* `Tim Carter`, `Chris van Rensburg`, `Ben Ilegbodu`, `Vinson Chuong`, original code contributed by `Zazzle Inc.`
*/

Uize.module ({
	name:'Uize.Widget.FormElement',
	required:[
		'Uize.Dom.Basics',
		'Uize.Dom.Event',
		'Uize.Dom.Classes',
		'Uize.Widget.FormElementWarning'
	],
	builder:function (_superclass) {
		'use strict';

		var
			/*** Variables for Scruncher Optimization ***/
				_true = true,
				_false = false,
				_null = null,
				_undefined,
				_Uize = Uize,
				_Uize_Dom_Basics = _Uize.Dom.Basics,
				_Uize_Dom_Event = _Uize.Dom.Event,
				_checkWarningShown = 'checkWarningShown',

				/*** validation/warning variables ***/
					_never = 'never',
					_tentativeValueChanged = 'tentativeValueChanged',
					_valueChanged = 'valueChanged',
					_validated = 'validated',
					_finished = 'finished',
					_validatedAfterFirstFinish = 'validatedAfterFirstFinish'
		;

		/*** Private Instance Methods ***/
			function _getInputNode (m) { return m.getNode('input') }

			function _getParentForm (m) {
				var
					_parentElementsWidget = m.parent,
					_parentForm
				;
				if (_parentElementsWidget && _parentElementsWidget.parent) {
					_parentForm = _parentElementsWidget.parent;

					if (!_parentForm.isForm)
						_parentForm = _null;
				}
				return _parentForm;
			}

			function _updateUiState (m) {
				if (m.isWired) {
					var _enabled = m.get ('enabledInherited') && !m.get ('busyInherited');
					m.setNodeProperties (_getInputNode(m), {disabled:!_enabled});
				}
			}

			function _updateUiValue (m) {
				if (m.isWired) {
					var
						_inputNode = _getInputNode(m),
						_value = m._value
					;
					_value != m.getNodeValue (_inputNode)
						&& m.setNodeValue (_inputNode, _value === _undefined ? '' : _value)
					;
				}
			}

			function _updateUiWarning () {
				var
					m = this,
					_warningShown = m._warningShown,
					_warningMessage = m._warningMessage
				;

				if (m.isWired) {
					// Update warning widget
					m._warningWidget &&
						m._warningWidget.set(
							_Uize.copyInto(
								{shown:_warningShown},
								_warningMessage ? {message:_warningMessage} : _null
							)
						);

					// visual indicators of warning state
					Uize.Dom.Classes.setState(
						[_getInputNode(m),m.getNode('label'),m.getNode ('shell')],
						m._errorClassName,
						_warningShown
					);
				}
			}

		return _superclass.subclass ({
			omegastructor:function () {
				var
					m = this,
					_warningWidget = m._warningWidget = m.addChild(
						'warning',
						m._warningWidgetClass || _Uize.Widget.FormElementWarning
					),
					_boundUpdateUiState = function () {_updateUiState(m)}
				;

				m.wire ({
					'Changed.busyInherited':_boundUpdateUiState,
					'Changed.enabledInherited':_boundUpdateUiState
				});

				m._isInitialized = _true;
				m._lastKeyDown = -1;
			},

			instanceMethods:{
				fireOkOnEnter:_Uize.returnTrue, // NOTE: can be overidden by subclasses

				checkIsEmpty:function () { return this._value == _null || this._value === '' },

				checkWarningShown:function () {
					var
						m = this,
						_warningShownWhen = m._warningShownWhen,
						_parentForm = _getParentForm(m),
						_currentWarningShown = m._warningShown
					;

					m.set({
						_warningShown:m._warningAllowedInherited
							&& m._isValid == _false
							&& (
								_parentForm
									? _parentForm.get('warningShown')
									: (
										m._isDirtyInherited
											&& (
												_warningShownWhen == _validated
												// keep the current warningShown value if warningShowWhen is set to a value
												// but that's not the current state
												|| (_warningShownWhen == _finished && (m._isFinished || _currentWarningShown))
												|| (_warningShownWhen == _validatedAfterFirstFinish && (m._finishedAtLeastOnce || _currentWarningShown))
											)
									)
							)
					});
				},

				getMoreValidators:_undefined, // To be overridden as necessary by subclasses (should return an array)

				getRootNode:function () { return this.getNode() || this.getNode('input') },

				restore:function () {
					this.set({
						_finishedAtLeastOnce:_false,
						_isDirty:'inherit',
						_value:this._initialValue
					});
				},

				updateUi:function () {
					var m = this;

					if (m.isWired) {
						_updateUiState(m);
						_updateUiValue(m);
						_updateUiWarning.call(m);

						_superclass.doMy (m,'updateUi');
					}
				},

				valueConformer:function (_value) {
					return _Uize.isFunction (this._valueConformer) ? this._valueConformer(_value) : _value;
				},

				validate:function () {
					var m = this;

					if (m._isInitialized) {
						var
							_validator = m._validator,
							_validators =
								(
									_Uize.isArray(_validator)
										? _validator
										: (_validator != _null ? [_validator] : _null)
								),
							_moreValidators = m.getMoreValidators ? m.getMoreValidators() : _null
						;

						if (_moreValidators)
							_validators = _validators ? _validators.concat(_moreValidators) : _moreValidators
						;

						var _setIsValid = function (_isValid) { m.set({_isValid:_isValid}) };

						if (_validators != _null) {
							var
								_value = m._validateWhen == _tentativeValueChanged
									? m._tentativeValue : m._value,
								_validatorsLength = _validators.length,
								_validatorNo = -1,
								_processNextValidator = function () {
									if (++_validatorNo < _validatorsLength) {
										var
											_handleIsValid = function (_isValid, _newWarningMessage) {
												if (_isValid == _false) {
													m.set({_warningMessage:_newWarningMessage || m._initialWarningMessage});
													_setIsValid(_false);
												}
												else _processNextValidator();
											},
											_validatorToEvaluate = _validators[_validatorNo],
											_validatorFunction = _validatorToEvaluate.func || (_Uize.isFunction (_validatorToEvaluate) ? _validatorToEvaluate : _null),
											_isValid = _validatorFunction
												? _validatorFunction.call(m, _value, _handleIsValid)
												: (
													_validatorToEvaluate instanceof RegExp
														? _validatorToEvaluate.test (_value)
														: _value == _validatorToEvaluate
												)
										;

										if (_isValid != _null)	// sign that the validation is asynchronous
											_handleIsValid(_isValid, _validatorToEvaluate.msg);
									}
									else _setIsValid(_true);
								}
							;

							_processNextValidator();
						}
						else _setIsValid(_true);
					}
				},

				wireUi:function () {
					var m = this;

					if (!m.isWired) {
						var _inputNode = _getInputNode(m);

						if (_inputNode) {
							/*** Set up the read-only state properties (attributes of the node) ***/
								m._type = _inputNode.type;
								m._elementName = _inputNode.name;

							var
								_fire = function (_eventName, _domEvent) { m.fire ({name:_eventName,domEvent:_domEvent}) },
								_fireClick = function (_event) { _fire ('Click', _event) },
								_fireKeyUp = function (_event) { _fire ('Key Up', _event) },
								_setValue = function (_isInitial) {
									m.set ({_value:m.getNodeValue(_inputNode)});
									!_isInitial && m._isDirty != _true &&
										m.set({_isDirty:_true});
								},
								_eventsToWire = {
									blur:function () {
										_setValue();
										m.set({_focused:_false});
									},
									focus:function () { m.set({_focused:_true}) },
									click:function (_event) {
										_setValue();
										_fireClick (_event);
									},
									keydown:function (_event) {
										m._lastKeyDown = _event.keyCode;
										_fire ('Key Down', _event);

										_Uize_Dom_Event.isKeyEnter(_event)
											&& m._type != 'textarea'
											&& _Uize_Dom_Event.abort(_event)
										;
									}
								}
							;

							// Build up events to wire
							switch (m._type) {
								case 'checkbox':
									break;

								case 'radio':	// operates on a group of like-named radio buttons, but one has to have the DOM node id
									m.set ({
										nodeMap:_Uize.copyInto(
											m.get('nodeMap') || {},
											{
												input:_Uize_Dom_Basics.find({
													tagName:'INPUT',
													type:'radio',
													name:m._elementName
												})
											}
										)
									});
									_inputNode = _getInputNode(m);
									break;

								case 'select-one':
								case 'select-multiple':
									_eventsToWire.change = _setValue;
									_eventsToWire.keyup = function (_event) {
										_setValue ();
										_fireKeyUp (_event);
									};
									_eventsToWire.click = _fireClick;
									break;

								default: // text, password, HTML5 text input, textarea, etc...
									_eventsToWire.keyup = function (_event) {
										// NOTE: When inputting Kanji, it's standard to use the enter button to choose kanji characters.
										// So everytime a user wants to type a multi-kanji word, 'Ok' would fire (which could ultimately
										// cause a form submission, which would be bad). So now the check to see if we should fire 'Ok'
										// checks to see if both keydown AND keyup are ENTER (since when you type kanji, you keydown on
										// a non-enter key, do stuff, then keyup to continue).
										if (m._type != 'textarea' && m._lastKeyDown == _event.keyCode && _Uize_Dom_Event.isKeyEnter (_event)) {
											_setValue ();
											m.fireOkOnEnter()
												&& _fire ('Ok', _event)
											;
										}
										else if (_Uize_Dom_Event.isKeyEscape (_event)) {
											_updateUiValue(m);		// replace with old (saved) value
											_fire ('Cancel', _event);
											_inputNode.blur();
										}
										else {
											m.set({
												_tentativeValue:m.getNodeValue(_inputNode),
												_isFinished:_false
											});
										}

										_fireKeyUp (_event);
									};
									_eventsToWire.click = _fireClick;
									break;
							}

							m.wireNode(_inputNode, _eventsToWire);

							// if no value was set, then grab the value from the node
							m._value === _undefined
								? _setValue(_true)
								: _updateUiValue(m)
							;
						}

						m.validate();

						_superclass.doMy (m,'wireUi');
					}
				}
			},

			stateProperties:{
				_elementName:'elementName', // read-only
					/*?
						State Properties
							elementName
								The name associated with the input nodes belonging to the form element.

								EXAMPLE
								........................................................
								<input id="myWidget-input" type='button' name='foobar'/>
								........................................................

								For a =Uize.Widget.FormElement= instance with the =idPrefix= of ='myWidget'= and the above HTML for its =input= DOM node, the value of the =elementName= state property will be ='foobar'=.
					*/
				_errorClassName:{
					name:'errorClassName',
					value:'error'
				},
				_focused:{
					name:'focused',
					onChange:function () {
						var
							m = this,
							_warningWidget = m.children.warning,
							_focused = m._focused
						;

						_warningWidget && _warningWidget.set({focused:_focused});

						if (m.isWired) {
							var _inputNode = _getInputNode(m);

							// If focused state property is out of sync with 'focused' state of the DOM node (via document.activeElement)
							// then force a focus.  We don't force a blur because blurring isn't as important (an apparently it causes
							// issues with Samsung Galaxy Tab 10.1)
							if (_focused && _inputNode && document.activeElement != _inputNode) {
								setTimeout(
									function () {
										try { (_Uize_Dom_Basics.isNode(_inputNode) ? _inputNode : _inputNode[0]).focus(); }
										catch(_ex) {}
									}, 50
								);

								// sync up the value with the UI, in case conformer had changed a UI value to something that already matched the programmatic value
								m.setNodeValue('input', m._value);
							}
						}
					},
					value:_false
				},
				_isEmpty:{
					name:'isEmpty',
					value:_true
				},
				_isDirty:{
					name:'isDirty',
					onChange:function () {
						var
							m = this,
							_parentForm = _getParentForm(m),
							_isDirty = m._isDirty == 'inherit'
								? (_parentForm ? _parentForm.get('isDirtyInherited') : _false)
								: m._isDirty
						;

						m.set({_isDirtyInherited:_isDirty});
					},
					value:'inherit'
				},
				_isDirtyInherited:{
					name:'isDirtyInherited',
					onChange:_checkWarningShown,
					value:_false
				},
				_isFinished:{
					name:'isFinished',
					onChange:function () {
						var m = this;

						if (m._isFinished && m._isInitialized) {
							m._validateWhen == _finished
								&& m.validate();
							m._finishedAtLeastOnce
								|| m.set({_finishedAtLeastOnce:_true});
						}

						m.checkWarningShown();
					},
					value:_true
				},
				_isValid:{
					name:'isValid',
					onChange:_checkWarningShown,
					value:_false
				},
				_tentativeValue:{	// readonly
					name:'tentativeValue',
					onChange: function () {
						var _parent = _getParentForm(this);
						while (_parent) {
							//Without this we could cause a form submit, see the
							//'Changed.tentativeValue' event in Form.js for explanation.
						    _parent.set({ isSubmitting: false });
						    _parent = _getParentForm(_parent);
						}
						this._validateWhen == _tentativeValueChanged
							&& this.validate()
						;
					},
					value:_null
				},
				_type:'type', // read-only
					/*?
						State Properties
							type
								The type associated with the input node belonging to the form element.

								EXAMPLE
								........................................................
								<input id="myWidget-input" type='button' name='foobar'/>
								........................................................

								For a =Uize.Widget.FormElement= instance with the =idPrefix= of ='myWidget'= and the above HTML for its =input= DOM node, the value of the =type= state property will be ='button'=.
					*/
				_validateWhen:{
					name:'validateWhen',
					value:_tentativeValueChanged	// valid values: 'never', 'tentativeValueChanged', 'valueChanged' & 'finished'
				},
				_validator:{
					name:'validator',
					onChange:'validate',
					value:_null
				},
				_value:{
					name:'value',
					conformer:function (_value) {
						var m = this;

						// conform the value to boolean if type is checkbox
						_value = m._type == 'checkbox' ? _value == _true : _value;

						return m.valueConformer(_value);
					},
					onChange:function () {
						var m = this;

						// As long as the widget is not wired & the value changes, update the initial value
						// so that when the widget is wired we have something restore to. We need to do it here
						// so that if value is set after construction but before wiring, we'll still capture
						// it as an initial value
						if (!m.isWired)
							m._initialValue = m._value
						;

						m.set({
							_tentativeValue:m._value, // sync tentative value
							_isFinished:_true,
							_isEmpty:m.checkIsEmpty()
						});

						m._validateWhen == _valueChanged
							&& m.validate();

						_updateUiValue(m);
					}/*,
					value:_null*/
				},
				_valueConformer:'valueConformer',
				_warningAllowed:{
					name:'warningAllowed',
					onChange:function () {
						var
							m = this,
							_parentForm = _getParentForm(m),
							_warningAllowed = m._warningAllowed == 'inherit'
								? (_parentForm ? _parentForm.get('warningAllowedInherited') : _true)
								: m._warningAllowed
						;

						m.set({_warningAllowedInherited:_warningAllowed});
					},
					value:'inherit'
				},
				_warningAllowedInherited:{
					name:'warningAllowedInherited',
					onChange:_checkWarningShown,
					value:_false
				},
				_warningMessage:{
					name:'warningMessage',
					onChange:[
						function () {
							var m = this;

							if (!m.isWired)
								m._initialWarningMessage = m._warningMessage;
						},
						_updateUiWarning
					]
				},
				_warningShown:{
					name:'warningShown',
					onChange:_updateUiWarning,
					value:_false
				},
				_warningShownWhen:{
					name:'warningShownWhen',
					onChange:_checkWarningShown,
					value:_validated	// valid values: 'validated', 'finished', validatedAfterFirstFinish'
				},
				_warningWidgetClass:'warningWidgetClass',

				/*** Private properties used for managing internal state w/ onChange functionality ***/
					_finishedAtLeastOnce:{
						onChange:_checkWarningShown,
						value:_false
					}
			}
		});
	}
});
//


koh5_pano



Your browser does not support the HTML5 canvas element.


Drag mouse to navigate.

Navigation





17.Aug.2010, Martin Wengenmayer