SOURCE CODE: Uize.Widget.Options.Accordion

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.Options.Accordion 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="=d" LineCompacting="TRUE"*/

/*?
  Introduction
    The =Uize.Widget.Options.Accordion= class extends its superclass by adding an accordion style behavior for revealing the contents of the selected tab.

    *DEVELOPERS:* `Jan Borgersen`

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

Uize.module ({
  name:'Uize.Widget.Options.Accordion',
  required:[
    'Uize.Node',
    'Uize.Fade'
  ],
  builder:function (_superclass) {
    /*** Variables for Scruncher Optimization ***/
      var
        _true = true,
        _false = false,
        _null = null,
        _Uize_Node = Uize.Node
      ;

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

            /*** Private Instance Properties ***/
              _this.fade = new Uize.Fade ({
                deceleration:1,
                duration:500
              });

            /*** initialization ***/
          }
        ),
        _classPrototype = _class.prototype
      ;

    /*** Private Instance Methods ***/
      _classPrototype._resolveToValueNo = function (_valueOrValueNo) {
        return _class.isNumber (_valueOrValueNo) ? _valueOrValueNo : this.getValueNoFromValue (_valueOrValueNo);
      };

      _classPrototype._getTabBodyNode = function (_valueOrValueNo) {
        return this.getNode ('option' + this._resolveToValueNo (_valueOrValueNo) + 'TabBody');
      };

      _classPrototype._updateUiOptionToggle = function(_optionNo, _isVisible) {
        var
          _optionButton = this.getOptionButton( _optionNo ),
          _toggleNode = _optionButton.getNode('toggle'),
          _toggleParentNode = _toggleNode ? _toggleNode.parentNode : _null,
          _toggleParentNodeDimensions = _toggleParentNode ? _Uize_Node.getDimensions(_toggleParentNode) : _null
        ;
        _toggleNode &&
          _Uize_Node.setStyle(_toggleNode, {top:_isVisible ? -_toggleParentNodeDimensions.height : 0})
        ;
      };

      _classPrototype._updateUiTabBodies = function (_showAnimation) {
        var
          _this = this,
          _valueNo = _this.get('valueNo'),
          _buttonHeights = _this._buttonHeights,
          _buttonHeightsLength = _buttonHeights.length,
          _top = 0
        ;
        if( _showAnimation ) {
          if( _valueNo != _this._lastShownTabBodyNo ) {
            _this.fade.stop();
            _this._growingNode = _this._getTabBodyNode( _valueNo );
            _this._shrinkingNode = _this._getTabBodyNode( _this._lastShownTabBodyNo );

            for (var _tabNo = -1; ++_tabNo < _buttonHeightsLength;) {
              _this.displayNode( _this._getTabBodyNode(_tabNo), (_tabNo == _valueNo || _tabNo == _this._lastShownTabBodyNo) );
              _this._updateUiOptionToggle(_tabNo, _valueNo == _tabNo);
            }

            _Uize_Node.setStyle(_this._growingNode,{height:1});
            _Uize_Node.setStyle(_this._shrinkingNode,{height:_this._maxTabHeight});
            _this.fade.start();
          }
        } else {
          for (var _tabNo = -1; ++_tabNo < _buttonHeightsLength;) {
            var _tabNode = _this._getTabBodyNode( _tabNo );
            _this.getOptionButton( _tabNo ).setNodeStyle('',{top:_top});
            _top += _buttonHeights[ _tabNo ];
            if( _valueNo == _tabNo ) {
              _this.displayNode(_tabNode);
              _Uize_Node.setStyle(_tabNode,{top:_top,height:_this._maxTabHeight});
              _top += _this._maxTabHeight;
            } else {
              _this.displayNode(_tabNode,_false);
            }
            _this._updateUiOptionToggle(_tabNo, _valueNo == _tabNo);
          }
        }
      };

    /*** Public Instance Methods ***/
      _classPrototype.getOptionButton = function (_valueOrValueNo) {
        return this.children ['option' + this._resolveToValueNo (_valueOrValueNo)];
      };

      _classPrototype.tabExists = function (_valueOrValueNo) {
        var _optionButton = this.getOptionButton (_valueOrValueNo);
        return (
          _optionButton && (_optionButton.getNode () || this._getTabBodyNode (_valueOrValueNo))
            ? true
            : false
        );
      };

      _classPrototype.wireUi = function () {
        var _this = this;
        if (!_this.isWired && !_this._deferWiring) {
          _superclass.prototype.wireUi.call (_this);

          /*** grab explicit heights and set absolute positioning ***/
            var
              _values = _this.get('values'),
              _shellHeight = _Uize_Node.getCoords( _this.getNode() ).height,
              _totalButtonHeights = 0
            ;
            _this._buttonHeights = [];
            for (
              var _valueNo = -1, _values = _this.get ('values'), _valuesLength = _values.length;
              ++_valueNo < _valuesLength;
            ) {
              var
                _buttonNode = _this.getOptionButton(_valueNo).getNode(),
                _tabNode = _this._getTabBodyNode( _valueNo )
              ;
              _this._buttonHeights[ _valueNo ] = _Uize_Node.getCoords( _buttonNode ).height;
              _totalButtonHeights += _this._buttonHeights[ _valueNo ];
              _Uize_Node.setStyle([_buttonNode,_tabNode],{position:'absolute'/*,overflow:'hidden'*/});
            }
            _this._maxTabHeight = _shellHeight - _totalButtonHeights;

          /*** setup ui ***/
            _this._growingTab = _this._shrinkingTab = _null;
            if( _this.get ('valueNo') < 0 )
              _this.set({valueNo:0})
            ;
            _this._lastShownTabBodyNo = _this.get ('valueNo');

            _this.fade.set ({
              startValue: 0,
              endValue: _this._maxTabHeight
            });
            _this.fade.wire ({
              'Changed.value':
                function () {
                  var
                    _valueNo = _this.get('valueNo'),
                    _top = 0,
                    _growingValue = +_this.fade,
                    _shrinkingValue = _this._maxTabHeight - _growingValue
                  ;
                  // first set heights
                  _Uize_Node.setStyle(_this._growingNode,{height:_growingValue});
                  _Uize_Node.setStyle(_this._shrinkingNode,{height:_shrinkingValue});
                  // then adjust positions
                  for(
                    var
                      _tabNo = -1,
                      _buttonHeights = _this._buttonHeights,
                      _buttonHeightsLength = _buttonHeights.length
                    ;
                    ++_tabNo < _buttonHeightsLength;
                  ) {
                    _this.getOptionButton( _tabNo ).setNodeStyle('', {top:_top});
                    _top += _buttonHeights[ _tabNo ];

                    if( _tabNo == _valueNo ) { // growing
                      _Uize_Node.setStyle(_this._growingNode,{top:_top});
                      _top += _growingValue;
                    } else if( _tabNo == _this._lastShownTabBodyNo ) { // shrinking
                      _Uize_Node.setStyle(_this._shrinkingNode,{top:_top});
                      _top += _shrinkingValue;
                    }
                  }
                },
              Done:
                function () {
                  _this.displayNode(_this._shrinkingNode,_false);
                  _this._lastShownTabBodyNo = _this.get('valueNo');
                }
            });

            _this.wire ('Changed.value',function () {
              _this._updateUiTabBodies (_true);
            });

            _this._updateUiTabBodies (_false);
        }
      };

    /*** Register Properties ***/
      _class.registerProperties ({
        _deferWiring:{
          name:'deferWiring',
          value:_false
        }
      });

    return _class;
  }
});