SOURCE CODE: Uize.Widget.FormElement

VIEW REFERENCE

/*______________
|       ______  |   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://uize.com
| /____/ /__/_| | COPYRIGHT : (c)2007-2009 UIZE
|          /___ |   LICENSE : Available under MIT License or GNU General Public License
|_______________|             http://uize.com/license.html
*/

/*ScruncherSettings Mappings="=c" LineCompacting="TRUE"*/

/*?
  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`

    The =Uize.Widget.FormElement= module defines the =Uize.Widget.FormElement= widget class, a subclass of =Uize.Widget=.
*/

Uize.module ({
  name:'Uize.Widget.FormElement',
  required:[
    'Uize.Node',
    'Uize.Node.Event'
  ],
  builder:function(_superclass){
    /*** Variables for Scruncher Optimization ***/
      var
        _null = null,
        _undefined
      ;

    /*** Class Constructor ***/
      var
        _class = _superclass.subclass (
          _null,
          function () {
            var _this = this;

            function _updateUiState () {_this._updateUiState ()}
            _this.wire ({
              'Changed.busyInherited':_updateUiState,
              'Changed.enabledInherited':_updateUiState
            });
          }
        ),
        _classPrototype = _class.prototype
      ;

    /*** Private Instance Methods ***/
      _classPrototype._getInputNode = function() { return this.getNode('input') };

      _classPrototype._updateUiElementName = function() {
        this.setNodeProperties(this._getInputNode(), {name:this._elementName})
      };

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

      _classPrototype._updateUiValue = function () {
        var _this = this;
        if (_this.isWired) {
          var
            _inputNode = _this._getInputNode(),
            _valueForNode = _this._value
          ;
          _valueForNode + '' != _this.getNodeValue (_inputNode) && _this.setNodeValue (_inputNode, _valueForNode);
        }
      };

    /*** Public Methods ***/
      _classPrototype.blur = function () {
        var _inputNode = this._getInputNode();
        _inputNode && (Uize.Node.isNode(_inputNode) ? _inputNode : _inputNode[0]).blur ();
        /*?
          Instance Methods
            blur
              Blurs the =input= implied node of the instance.

              SYNTAX
              ......................
              myFormElement.blur ();
              ......................

              NOTES
              - see the companion =focus= instance method
        */
      };

      _classPrototype.getProperties = function() {
        var
          _this = this,
          _type = _this._type,
          _value = _this._value,
          _properties = {value:_value}
        ;

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

          if (_type == 'select-one') {
            var _selectedIndex = _inputNode.selectedIndex;

            Uize.copyInto(
              _properties,
              {
                selectedIndex:_selectedIndex,
                text:_selectedIndex > -1 ? _inputNode.options[_selectedIndex].text : _null
              }
            );
          }
          else if (_type == 'select-multiple') {
            _properties = [];

            var
              _inputNodeOptions = _inputNode.options,
              _optionNo = 0,
              _optionsLength = _inputNodeOptions.length,
              _option
            ;

            for (; _optionNo < _optionsLength; _optionNo++)
              (_option = _inputNodeOptions [_optionNo]).selected &&
                _properties.push ({
                  value:_option.value,
                  selectedIndex:_option.selectedIndex,
                  title:_option.title
                })
            ;
          }
        }

        return _properties;
      };

      _classPrototype.focus = function () {
        var _inputNode = this._getInputNode();
        _inputNode && (Uize.Node.isNode(_inputNode) ? _inputNode : _inputNode[0]).focus ();
        /*?
          Instance Methods
            focus
              Focuses the =input= implied node of the instance.

              SYNTAX
              .......................
              myFormElement.focus ();
              .......................

              NOTES
              - see the companion =blur= instance method
        */
      };

      _classPrototype.updateUi = function () {
        this._updateUiElementName();
        this._updateUiState ();
        this._updateUiValue ();
      };

      _classPrototype.wireUi = function() {
        var _this = this;

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

          if (_inputNode) {
            /*** Set up the set-get properties (attributes of the node) ***/
              _this._type = _inputNode.type;  // this property is read-only

              if (_this._elementName == _undefined)
                _this._elementName = _inputNode.name
              ;
            function _fire (_event) { _this.fire (_event) }
            function _fireClicked () { _fire ('Clicked') }
            function _fireBlur () { _fire ('Blur') }
            function _fireFocus () { _fire ('Focus') }
            function _fireKeyUp () { _fire ('Key Up') }
            function _setValue () { _this.set ({_value:_this.getNodeValue(_inputNode)}) }

            switch (_this._type) {
              case 'text':
              case 'textarea':
              case 'password':
                _this.wireNode (
                  _inputNode,
                  {
                    change:function () { _setValue() },
                    keyup:function (_domEvent) {
                      if (Uize.Node.Event.isKeyEnter (_domEvent) && _this._type != 'textarea')
                        _fire ({name:'Ok',domEvent:_domEvent});
                      else if (Uize.Node.Event.isKeyEscape (_domEvent))
                        _fire ({name:'Cancel',domEvent:_domEvent});

                      _setValue ();
                      _fireKeyUp ();
                    },
                    blur:function () {
                      _setValue ();    // do we need to do this?
                      _fireBlur ();
                    },
                    focus:function () {
                      _setValue ();    // do we need to do this?
                      _fireFocus ();
                    }
                  }
                );
                break;

              case 'hidden':
                _this.wireNode (_inputNode,'change',_setValue);
                break;

              case 'radio':  // operates on a group of like-named radio buttons, but one has to have the implied node id
                _this.set ({
                  nodeMap:_class.copyInto(
                    _this.get('nodeMap') || {},
                    {
                      input:_inputNode = Uize.Node.find({
                        tagName:'INPUT',
                        type:'radio',
                        name:_inputNode.name
                      })
                    }
                  )
                });

              case 'checkbox':  // radio and checkbox do the same at this point
                _this.wireNode (
                  _inputNode,
                  {
                    blur:_fireBlur,
                    click:function () {
                      _setValue();
                      _fireClicked ();
                    },
                    focus:_fireFocus
                  }
                );
                break;

              case 'select-one':
              case 'select-multiple':
                _this.wireNode (
                  _inputNode,
                  {
                    blur:_fireBlur,
                    click:function () {
                      _setValue ();
                      _fireClicked ();
                    },
                    keyup:function () {
                      _setValue ();
                      _fireKeyUp ();
                    },
                    focus:_fireFocus
                  }
                );
                break;
            }

            _this._value == _undefined && _setValue();
          }

          _superclass.prototype.wireUi.call (_this);
        }
      };

    /*** Register Properties ***/
      _class.registerProperties({
        _elementName:'elementName', // read-only
          /*?
            Set-get Properties
              elementName
                The name associated with the input nodes belonging to the form element.

                EXAMPLE
                ........................................................
                
                ........................................................

                For a =Uize.Widget.FormElement= instance with the =idPrefix= of ='myWidget'= and the above HTML for its =input= implied node, the value of the =elementName= set-get property will be ='foobar'=.
          */
        _type:'type', // read-only
          /*?
            Set-get Properties
              type
                The type associated with the input node belonging to the form element.

                EXAMPLE
                ........................................................
                
                ........................................................

                For a =Uize.Widget.FormElement= instance with the =idPrefix= of ='myWidget'= and the above HTML for its =input= implied node, the value of the =type= set-get property will be ='button'=.
          */
        _value:{
          name:'value',
          onChange:_classPrototype._updateUiValue,
          value:_null
          /*?
            Set-get Properties
              value
                The value associated with the input node(s) belonging to the form element. The format of the value will differ depending on the type of the form element.

                The following is a list matching form element types with their value formats:

                - text, hidden, password, textarea --> node.value
                - select-one --> {text, index, optionValue}
                - select-multiple --> [{text, index, optionValue}*]
                - radiobutton --> value of the selected radio button
                - checkbox --> {checked, value}
          */
        }
      });

    return _class;
  }
});