Ext.ux.Accordion extension class code:
    1 // vim: ts=2:sw=2:nu:fdc=4:nospell
    2 
    3 // Create user extensions namespace (Ext.ux)
    4 Ext.namespace('Ext.ux');
    5 
    6 /**
    7   * Ext.ux.Accordion Extension Class
    8   *
    9   * @author  Ing. Jozef Sakalos
   10   * @version $Id: Ext.ux.Accordion.js 152 2007-08-21 17:46:03Z jozo $
   11   *
   12   * @class Ext.ux.Accordion
   13   * @extends Ext.ContentPanel
   14   * @constructor
   15   * @param {String/HTMLElement/Element} el The container element for this panel
   16   * @param {String/Object} config A string to set only the title or a config object
   17   * @cfg {Boolean} animate global animation flag for all panels. (defaults to true)
   18   * @cfg {Boolean} boxWrap set to true to wrap wrapEl the body is child of (defaults to false)
   19   * @cfg {Boolean} draggable set to false to disallow panels dragging (defaults to true)
   20   * @cfg {Boolean} fitHeight set to true if you use fixed height dock
   21   * @cfg {Boolean} forceOrder set to true if to disable reordering of panels (defaults to false)
   22   * @cfg {Boolean} independent true to make panels independent (defaults to false)
   23   * @cfg {Integer} initialHeight Initial height to set box to (defaults to 0)
   24   * @cfg {Boolean} keepState Set to false to exclude this accordion from state management (defaults to true)
   25   * @cfg {Boolean} monitorWindowResize if true panels are moved to
   26   *  viewport if window is small (defaults to true)
   27   * @cfg {Boolean} resizable global resizable flag for all panels (defaults to true)
   28   * @cfg {Boolean} undockable true to allow undocking of panels (defaults to true)
   29   * @cfg {Boolean} useShadow global useShadow flag for all panels. (defaults to true)
   30   * @cfg {Element/HTMLElement/String} wrapEl Element to wrap with nice surrounding
   31   */
   32 Ext.ux.Accordion = function(el, config) {
   33 
   34   // call parent constructor
   35   Ext.ux.Accordion.superclass.constructor.call(this, el, config);
   36 
   37   // create collection for panels
   38   this.items = new Ext.util.MixedCollection();
   39 
   40   // assume no panel is expanded
   41   this.expanded = null;
   42 
   43   // {{{
   44   // install event handlers
   45   this.on({
   46 
   47     // {{{
   48     // runs before expansion. Triggered by panel's beforeexpand event
   49     beforeexpand: {
   50         scope: this
   51       , fn: function(panel) {
   52           // raise panel above others
   53           if(!panel.docked) {
   54             this.raise(panel);
   55           }
   56 
   57           // set fixed height
   58           panel.autoSize();
   59 //          var panelBodyHeight;
   60 //          if(this.fitHeight && panel.docked) {
   61 //            panelBodyHeight = this.getPanelBodyHeight();
   62 //            if(panelBodyHeight) {
   63 //              panel.body.setHeight(panelBodyHeight);
   64 //            }
   65 //          }
   66 
   67           if(panel.docked) {
   68             this.expandCount++;
   69             this.expanding = true;
   70 //            this.setDockScroll(false);
   71           }
   72 
   73           // don't collapse others if independent or not docked
   74           if(this.independent || !panel.docked) {
   75             return this;
   76           }
   77 
   78           // collapse expanded panel
   79           if(this.expanded && this.expanded.docked) {
   80             this.expanded.collapse();
   81           }
   82 
   83           // remember this panel as expanded
   84           this.expanded = panel;
   85     }}
   86     // }}}
   87     // {{{
   88     // runs before panel collapses. Triggered by panel's beforecollapse event
   89     , beforecollapse: {
   90         scope: this
   91       , fn: function(panel) {
   92 
   93         // raise panel if not docked
   94         if(!panel.docked) {
   95           this.raise(panel);
   96         }
   97         return this;
   98     }}
   99     // }}}
  100     // {{{
  101     // runs on when panel expands (before animation). Triggered by panel's expand event
  102     , expand: {
  103         scope: this
  104       , fn: function(panel) {
  105         if(this.hideOtherOnExpand) {
  106           this.hideOther(panel);
  107         }
  108         this.fireEvent('panelexpand', panel);
  109     }}
  110     // }}}
  111     // {{{
  112     // runs on when panel collapses (before animation). Triggered by panel's collapse event
  113     , collapse: {
  114         scope: this
  115       , fn: function(panel) {
  116         this.fireEvent('panelcollapse', panel);
  117     }}
  118     // }}}
  119     // {{{
  120     // runs on when animation is completed. Triggered by panel's animationcompleted event
  121     , animationcompleted: {
  122       scope: this
  123       , fn: function(panel) {
  124         var box = panel.el.getBox();
  125         this.expandCount = (this.expandCount && this.expanding) ? --this.expandCount : 0;
  126         if((0 === this.expandCount) && this.expanding) {
  127 //          this.setDockScroll(true);
  128           this.expanding = false;
  129         }
  130         if(this.hideOtherOnExpand) {
  131           if(panel.collapsed && panel.docked) {
  132             this.showOther(panel);
  133           }
  134 //          else if(panel.docked) {
  135 //            this.hideOther(panel);
  136 //          }
  137         }
  138 //        this.fireEvent('panelbox', panel, box);
  139     }}
  140     // }}}
  141     // {{{
  142     // runs when panel is pinned. Triggered by panel's pinned event
  143     , pinned: {
  144         scope: this
  145       , fn: function(panel, pinned) {
  146         if(!pinned) {
  147           if(panel.collapseOnUnpin) {
  148             panel.collapse();
  149           }
  150           else if(!this.independent) {
  151             this.items.each(function(p) {
  152               if(p !== panel && p.docked && !p.pinned) {
  153                 p.collapse();
  154               }
  155             });
  156             this.expanded = panel;
  157           }
  158         }
  159         if(this.hideOtherOnExpand) {
  160           if(panel.docked && pinned) {
  161             this.showOther(panel);
  162           }
  163         }
  164         this.fireEvent('panelpinned', panel, pinned);
  165     }}
  166     // }}}
  167 
  168     , destroy: {
  169       scope:this
  170       , fn: function(panel) {
  171         this.items.removeKey(panel.id);
  172         this.updateOrder();
  173     }}
  174   });
  175   // }}}
  176   // {{{
  177   // add events
  178   this.addEvents({
  179     /**
  180       * Fires when a panel of the dock is collapsed
  181       * @event panelcollapse
  182       * @param {Ext.ux.InfoPanel} panel
  183       */
  184     panelcollapse: true
  185 
  186     /**
  187       * Fires when a panel of the dock is expanded
  188       * @event panelexpand
  189       * @param {Ext.ux.InfoPanel} panel
  190       */
  191     , panelexpand: true
  192 
  193     /**
  194       * Fires when a panel of the dock is pinned
  195       * @event panelpinned
  196       * @param {Ext.ux.InfoPanel} panel
  197       * @param {Boolean} pinned true if panel was pinned false if unpinned
  198       */
  199     , panelpinned: true
  200 
  201     /**
  202       * Fires when the independent state of dock changes
  203       * @event independent
  204       * @param {Ext.ux.Accordion} this
  205       * @param {Boolean} independent New independent state
  206       */
  207     , independent: true
  208 
  209     /**
  210       * Fires when the order of panel is changed
  211       * @event orderchange
  212       * @param {Ext.ux.Accordion} this
  213       * @param {Array} order New order array
  214       */
  215     , orderchange: true
  216 
  217     /**
  218       * Fires when the undockable state of dock changes
  219       * @event undockable
  220       * @param {Ext.ux.Accordion} this
  221       * @param {Array} undockable New undockable state
  222       */
  223     , undockable: true
  224 
  225     /**
  226       * Fires when a panel is undocked
  227       * @event panelundock
  228       * @param {Ext.ux.InfoPanel} panel
  229       * @param {Object} box Position and size object
  230       */
  231     , panelundock: true
  232 
  233     /**
  234       * Fires when a panel is undocked
  235       * @event paneldock
  236       * @param {Ext.ux.InfoPanel} panel
  237       */
  238     , paneldock: true
  239 
  240     /**
  241       * Fires when a panel box is changed, e.g. after dragging
  242       * @event panelbox
  243       * @param {Ext.ux.InfoPanel} panel
  244       * @param {Object} box Position and size object
  245       */
  246     , panelbox: true
  247 
  248     /**
  249       * Fires when useShadow status changes
  250       * @event useshadow
  251       * @param {Ext.ux.Accordion} this
  252       * @param {Boolean} shadow Use shadow (for undocked panels) flag
  253       */
  254     , useshadow: true
  255 
  256     /**
  257       * Fires before the panel is detached from this accordion. Return false to cancel the detach
  258       * @event beforedetach
  259       * @param {Ext.ux.Accordion} this
  260       * @param {Ext.ux.InfoPanel} panel being detached
  261       */
  262     , beforedetach: true
  263 
  264     /**
  265       * Fires after the panel has been detached from this accordion
  266       * @event detach
  267       * @param {Ext.ux.Accordion} this
  268       * @param {Ext.ux.InfoPanel} panel detached panel
  269       */
  270     , detach: true
  271 
  272     /**
  273       * Fires before the panel is attached from this accordion. Return false to cancel the attach
  274       * @event beforeattach
  275       * @param {Ext.ux.Accordion} this
  276       * @param {Ext.ux.InfoPanel} panel being attached
  277       */
  278     , beforeattach: true
  279 
  280     /**
  281       * Fires after the panel is attached to this accordion
  282       * @event attach
  283       * @param {Ext.ux.Accordion} this
  284       * @param {Ext.ux.InfoPanel} panel attached panel
  285       */
  286     , attach: true
  287 
  288   });
  289   // }}}
  290 
  291   // setup body
  292   this.body = Ext.get(this.body) || this.el;
  293   this.resizeEl = this.body;
  294   this.id = this.id || this.body.id;
  295   this.body.addClass('x-dock-body');
  296 
  297   // setup desktop
  298   this.desktop = Ext.get(this.desktop || document.body);
  299   //this.desktop = this.desktop.dom || this.desktop;
  300 
  301   // setup fixed hight
  302   this.wrapEl = Ext.get(this.wrapEl);
  303   if(this.fitHeight) {
  304     this.body.setStyle('overflow', 'hidden');
  305 //    this.bodyHeight = this.initialHeight || this.body.getHeight();
  306     this.body.setHeight(this.initialHeight || this.body.getHeight());
  307     if(this.boxWrap && this.wrapEl) {
  308       this.wrapEl.boxWrap();
  309     }
  310   }
  311 
  312   // watch window resize
  313   if(this.monitorWindowResize) {
  314     Ext.EventManager.onWindowResize(this.adjustViewport, this);
  315   }
  316 
  317   // create drop zone for panels
  318   this.dd = new Ext.dd.DropZone(this.body.dom, {
  319     ddGroup: this.ddGroup || 'dock-' + this.id
  320   });
  321 
  322   Ext.ux.AccordionManager.add(this);
  323 
  324 }; // end of constructor
  325 
  326 // extend
  327 Ext.extend(Ext.ux.Accordion, Ext.ContentPanel, {
  328 
  329   // {{{
  330   // defaults
  331   independent: false
  332   , undockable: true
  333   , useShadow: true
  334   , boxWrap: false
  335   , fitHeight: false
  336   , initialHeight: 0
  337   , animate: true // global animation flag
  338   , expandCount: 0
  339   , expanding: false
  340   , monitorWindowResize: true
  341   , resizable: true // global resizable flag
  342   , draggable: true // global draggable flag
  343   , forceOrder: false
  344   , keepState: true
  345   , hideOtherOnExpand: false
  346   // }}}
  347   // {{{
  348   /**
  349     * Adds the panel to Accordion
  350     * @param {Ext.ux.InfoPanel} panel Panel to add
  351     * @return {Ext.ux.InfoPanel} added panel
  352     */
  353   , add: function(panel) {
  354 
  355     // append panel to body
  356     this.body.appendChild(panel.el);
  357 
  358     this.attach(panel);
  359 
  360     // panel dragging
  361     if(undefined === panel.draggable && this.draggable) {
  362       panel.draggable = true;
  363       panel.dd = new Ext.ux.Accordion.DDDock(panel, this.ddGroup || 'dock-' + this.id, this);
  364     }
  365 
  366     // panel resizing
  367     if(undefined === panel.resizable && this.resizable) {
  368       panel.resizable = true;
  369 //      panel.setResizable(true);
  370     }
  371 
  372     // panel shadow
  373     panel.useShadow = undefined === panel.useShadow ? this.useShadow : panel.useShadow;
  374     panel.setShadow(panel.useShadow);
  375     if(panel.shadow) {
  376       panel.shadow.hide();
  377     }
  378 
  379     // panel animation
  380     panel.animate = undefined === panel.animate ? this.animate : panel.animate;
  381 
  382     // z-index for panel
  383     panel.zindex = Ext.ux.AccordionManager.getNextZindex();
  384 
  385     panel.docked = true;
  386     panel.desktop = this.desktop;
  387 
  388     if(false === panel.collapsed) {
  389       panel.collapsed = true;
  390       panel.expand(true);
  391     }
  392     return panel;
  393 
  394   }
  395   // }}}
  396   // {{{
  397   /**
  398     * attach panel to this accordion
  399     * @param {Ext.ux.InfoPanel} panel panel to attach
  400     * @return {Ext.ux.Accordion} this
  401     */
  402   , attach: function(panel) {
  403 
  404     // fire beforeattach event
  405     if(false === this.fireEvent('beforeattach', this, panel)) {
  406       return this;
  407     }
  408 
  409     // add panel to items
  410     this.items.add(panel.id, panel);
  411 
  412     // install event handlers
  413     this.installRelays(panel);
  414     panel.bodyClickDelegate = this.onClickPanelBody.createDelegate(this, [panel]);
  415     panel.body.on('click', panel.bodyClickDelegate);
  416 
  417     // set panel dock
  418     panel.dock = this;
  419 
  420     // add docked class to panel body
  421     panel.body.replaceClass('x-dock-panel-body-undocked', 'x-dock-panel-body-docked');
  422 
  423     // repair panel height
  424     panel.autoSize();
  425     if(this.fitHeight) {
  426       this.setPanelHeight(panel);
  427     }
  428 
  429     // fire attach event
  430     this.fireEvent('attach', this, panel);
  431 
  432     return this;
  433   }
  434   // }}}
  435   // {{{
  436   /**
  437     * detach panel from this accordion
  438     * @param {Ext.ux.InfoPanel} panel to detach
  439     * @return {Ext.ux.Accordion} this
  440     */
  441   , detach: function(panel) {
  442 
  443     // fire beforedetach event
  444     if(false === this.fireEvent('beforedetach', this, panel)) {
  445       return this;
  446     }
  447 
  448     // unhook events from panel
  449     this.removeRelays(panel);
  450     panel.body.un('click', panel.bodyClickDelegate);
  451 
  452     // remove panel from items
  453     this.items.remove(panel);
  454     panel.dock = null;
  455 
  456     // remove docked class from panel body
  457     panel.body.replaceClass('x-dock-panel-body-docked', 'x-dock-panel-body-undocked');
  458 
  459     // repair expanded property
  460     if(this.expanded === panel) {
  461       this.expanded = null;
  462     }
  463 
  464     // repair panel height
  465     panel.autoSize();
  466     if(this.fitHeight) {
  467       this.setPanelHeight();
  468     }
  469 
  470     // fire detach event
  471     this.fireEvent('detach', this, panel);
  472 
  473     return this;
  474   }
  475   // }}}
  476   // {{{
  477   /**
  478     * Called internally to raise panel above others
  479     * @param {Ext.ux.InfoPanel} panel Panel to raise
  480     * @return {Ext.ux.InfoPanel} panel Panel that has been raised
  481     */
  482   , raise: function(panel) {
  483     return Ext.ux.AccordionManager.raise(panel);
  484   }
  485   // }}}
  486   // {{{
  487   /**
  488     * Resets the order of panels within the dock
  489     *
  490     * @return {Ext.ux.Accordion} this
  491     */
  492   , resetOrder: function() {
  493     this.items.each(function(panel) {
  494       if(!panel.docked) {
  495         return;
  496       }
  497       this.body.appendChild(panel.el);
  498     }, this);
  499     this.updateOrder();
  500     return this;
  501   }
  502   // }}}
  503   // {{{
  504   /**
  505     * Called internally to update the order variable after dragging
  506     */
  507   , updateOrder: function() {
  508     var order = [];
  509     var titles = this.body.select('div.x-layout-panel-hd');
  510     titles.each(function(titleEl) {
  511       order.push(titleEl.dom.parentNode.id);
  512     });
  513     this.order = order;
  514     this.fireEvent('orderchange', this, order);
  515   }
  516   // }}}
  517   // {{{
  518   /**
  519     * Returns array of panel ids in the current order
  520     * @return {Array} order of panels
  521     */
  522   , getOrder: function() {
  523     return this.order;
  524   }
  525   // }}}
  526   // {{{
  527   /**
  528     * Set the order of panels
  529     * @param {Array} order Array of ids of panels in required order.
  530     * @return {Ext.ux.Accordion} this
  531     */
  532   , setOrder: function(order) {
  533     if('object' !== typeof order || undefined === order.length) {
  534       throw "setOrder: Argument is not array.";
  535     }
  536     var panelEl, dock, panelId, panel;
  537     for(var i = 0; i < order.length; i++) {
  538       panelId = order[i];
  539       dock = Ext.ux.AccordionManager.get(panelId);
  540       if(dock && dock !== this) {
  541         panel = dock.items.get(panelId);
  542         dock.detach(panel);
  543         this.attach(panel);
  544       }
  545       panelEl = Ext.get(panelId);
  546       if(panelEl) {
  547         this.body.appendChild(panelEl);
  548       }
  549     }
  550     this.updateOrder();
  551     return this;
  552   }
  553   // }}}
  554   // {{{
  555   /**
  556     * Collapse all docked panels
  557     * @param {Boolean} alsoPinned true to first unpin then collapse
  558     * @param {Ext.ux.InfoPanel} except This panel will not be collapsed.
  559     * @return {Ext.ux.Accordion} this
  560     */
  561   , collapseAll: function(alsoPinned, except) {
  562     this.items.each(function(panel) {
  563       if(panel.docked) {
  564         panel.pinned = alsoPinned ? false : panel.pinned;
  565         if(!except || panel !== except) {
  566           panel.collapse();
  567         }
  568       }
  569     }, this);
  570     return this;
  571   }
  572   // }}}
  573   // {{{
  574   /**
  575     * Expand all docked panels in independent mode
  576     * @return {Ext.ux.Accordion} this
  577     */
  578   , expandAll: function() {
  579     if(this.independent) {
  580       this.items.each(function(panel) {
  581         if(panel.docked && panel.collapsed) {
  582           panel.expand();
  583         }
  584       }, this);
  585     }
  586   }
  587   // }}}
  588   // {{{
  589   /**
  590     * Called internally while dragging and by state manager
  591     * @param {Ext.ux.InfoPanel/String} panel Panel object or id of the panel
  592     * @box {Object} box coordinates with target position and size
  593     * @return {Ext.ux.Accordion} this
  594     */
  595   , undock: function(panel, box) {
  596 
  597     // get panel if necessary
  598     panel = 'string' === typeof panel ? this.items.get(panel) : panel;
  599 
  600     // proceed only if we have docked panel and in undockable mode
  601     if(panel && panel.docked && this.undockable) {
  602 
  603       // sanity check
  604       if(box.x < 0 || box.y < 0) {
  605         return this;
  606       }
  607 
  608       // todo: check this
  609 //      if(panel.collapsed) {
  610 //        box.height = panel.lastHeight || panel.maxHeight || box.height;
  611 //      }
  612 
  613       // move the panel in the dom (append to desktop)
  614       this.desktop.appendChild(panel.el.dom);
  615 
  616       // adjust panel visuals
  617       panel.el.applyStyles({
  618         position:'absolute'
  619         , 'z-index': panel.zindex
  620       });
  621       panel.body.replaceClass('x-dock-panel-body-docked', 'x-dock-panel-body-undocked');
  622 
  623       // position the panel
  624       panel.setBox(box);
  625 
  626       // reset docked flag
  627       panel.docked = false;
  628 
  629       // hide panel shadow (will be shown by raise)
  630       if(panel.shadow) {
  631         panel.shadow.hide();
  632       }
  633 
  634       // raise panel above others
  635       this.raise(panel);
  636 
  637       panel.autoSize();
  638 
  639       if(panel === this.expanded) {
  640         this.expanded = null;
  641       }
  642 
  643       // set the height of a docked expanded panel
  644       this.setPanelHeight(this.expanded);
  645 
  646       // enable resizing and scrolling
  647       panel.setResizable(!panel.collapsed);
  648 
  649       // remember size of the undocked panel
  650       panel.lastWidth = box.width;
  651 //      panel.lastHeight = box.height;
  652 
  653       // fire panelundock event
  654       this.fireEvent('panelundock', panel, {x:box.x, y:box.y, width:box.width, height:box.height});
  655 
  656 //      this.updateOrder();
  657     }
  658 
  659     return this;
  660   }
  661   // }}}
  662   // {{{
  663   /**
  664     * Called internally while dragging
  665     * @param {Ext.ux.InfoPanel/String} panel Panel object or id of the panel
  666     * @param {String} targetId id of panel after which this panel will be docked
  667     * @return {Ext.ux.Accordion} this
  668     */
  669   , dock: function(panel, targetId) {
  670 
  671     // get panel if necessary
  672     panel = 'string' === typeof panel ? this.items.get(panel) : panel;
  673 
  674     // proceed only if we have a docked panel
  675     var dockWidth, newTargetId, panelHeight, idx, i, targetPanel;
  676     if(panel && !panel.docked) {
  677 
  678       // find correct target if order is forced
  679       if(this.forceOrder) {
  680         idx = this.items.indexOf(panel);
  681         for(i = idx + 1; i < this.items.getCount(); i++) {
  682           targetPanel = this.items.itemAt(i);
  683           if(targetPanel.docked) {
  684             newTargetId = targetPanel.id;
  685             break;
  686           }
  687         }
  688         targetId = newTargetId || this.id;
  689       }
  690 
  691       // remember width and height
  692       if(!panel.collapsed) {
  693 //        panel.lastWidth = panel.el.getWidth();
  694 //        panel.lastHeight = panel.el.getHeight();
  695         if(!this.independent && this.expanded) {
  696           this.expanded.collapse();
  697         }
  698         this.expanded = panel;
  699       }
  700 
  701       dockWidth = this.body.getWidth(true);
  702 
  703       // move the panel element in the dom
  704       if(targetId && (this.body.id !== targetId)) {
  705         panel.el.insertBefore(Ext.fly(targetId));
  706       }
  707       else {
  708         panel.el.appendTo(this.body);
  709       }
  710 
  711       // set docked flag
  712       panel.docked = true;
  713 
  714       // adjust panel visuals
  715       panel.body.replaceClass('x-dock-panel-body-undocked', 'x-dock-panel-body-docked');
  716       panel.el.applyStyles({
  717         top:''
  718         , left:''
  719         , width:''
  720 //        , height:''
  721         , 'z-index':''
  722         , position:'relative'
  723         , visibility:''
  724       });
  725       panel.body.applyStyles({width: Ext.isIE ? dockWidth + 'px' : '', height:''});
  726 
  727       panel.autoSize();
  728 //      if(!this.fitHeight) {
  729 //        panelHeight = panel.fixedHeight || panel.maxHeight;
  730 //        if(panelHeight) {
  731 //          panel.setHeight(panelHeight);
  732 //        }
  733 //      }
  734 
  735       // disable resizing and shadow
  736       panel.setResizable(false);
  737       if(panel.shadow) {
  738         panel.shadow.hide();
  739       }
  740 
  741       // set panel height (only if this.fitHeight = true)
  742       this.setPanelHeight(panel.collapsed ? this.expanded : panel);
  743 
  744       // fire paneldock event
  745       this.fireEvent('paneldock', panel);
  746 
  747 //      this.updateOrder();
  748     }
  749 
  750     return this;
  751   }
  752   // }}}
  753   // {{{
  754   /**
  755     * Moves panel from this dock (accordion) to another
  756     * @param {Ext.ux.InfoPanel} panel Panel to move
  757     * @param {Ext.ux.Accordion} targetDock Dock to move to
  758     */
  759   , moveToDock: function(panel, targetDock) {
  760     this.detach(panel);
  761     targetDock.attach(panel);
  762     panel.docked = false;
  763     targetDock.dock(panel);
  764     this.setPanelHeight();
  765     this.updateOrder();
  766     targetDock.updateOrder();
  767   }
  768   // }}}
  769   // {{{
  770   /**
  771     * Sets the independent mode
  772     * @param {Boolean} independent set to false for normal mode
  773     * @return {Ext.ux.Accordion} this
  774     */
  775   , setIndependent: function(independent) {
  776     this.independent = independent ? true : false;
  777     this.fireEvent('independent', this, independent);
  778     return this;
  779   }
  780   // }}}
  781   // {{{
  782   /**
  783     * Sets the undockable mode
  784     * If undockable === true all undocked panels are docked and collapsed (except pinned)
  785     * @param {Boolean} undockable set to true to not allow undocking
  786     * @return {Ext.ux.Accordion} this
  787     */
  788   , setUndockable: function(undockable) {
  789     this.items.each(function(panel) {
  790 
  791       // dock and collapse (except pinned) all undocked panels if not undockable
  792       if(!undockable && !panel.docked) {
  793         this.dock(panel);
  794         if(!this.independent && !panel.collapsed && !panel.pinned) {
  795           panel.collapse();
  796         }
  797       }
  798 
  799       // refresh dragging constraints
  800       if(panel.docked && panel.draggable) {
  801         panel.dd.constrainTo(this.body, 0, false);
  802         panel.dd.clearConstraints();
  803         if(undockable) {
  804           panel.constrainToDesktop();
  805         }
  806         else {
  807           panel.dd.setXConstraint(0,0);
  808         }
  809       }
  810     }, this);
  811 
  812     // set the flag and fire event
  813     this.undockable = undockable;
  814     this.fireEvent('undockable', this, undockable);
  815     return this;
  816   }
  817   // }}}
  818   // {{{
  819   /**
  820     * Sets the shadows for all panels
  821     * @param {Boolean} shadow set to false to disable shadows
  822     * @return {Ext.ux.Accordion} this
  823     */
  824   , setShadow: function(shadow) {
  825     this.items.each(function(panel) {
  826       panel.useShadow = shadow;
  827       panel.setShadow(false);
  828       if(!panel.docked) {
  829         panel.setShadow(shadow);
  830       }
  831     });
  832     this.useShadow = shadow;
  833     this.fireEvent('useshadow', this, shadow);
  834     return this;
  835   }
  836   // }}}
  837 // {{{
  838   /**
  839     * Called when user clicks the panel body
  840     * @param {Ext.ux.InfoPanel} panel
  841     */
  842   , onClickPanelBody: function(panel) {
  843     if(!panel.docked) {
  844       this.raise(panel);
  845     }
  846   }
  847   // }}}
  848   // {{{
  849   /**
  850     * Called internally for fixed height docks to get current height of panel(s)
  851     */
  852   , getPanelBodyHeight: function() {
  853       var titleHeight = 0;
  854       this.items.each(function(panel) {
  855         titleHeight += panel.docked ? panel.titleEl.getHeight() : 0;
  856       });
  857       this.panelBodyHeight = this.body.getHeight() - titleHeight - this.body.getFrameWidth('tb') + 1;
  858 //      this.panelBodyHeight = this.body.getHeight() - titleHeight - this.body.getFrameWidth('tb');
  859       return this.panelBodyHeight;
  860   }
  861   // }}}
  862   // {{{
  863   /**
  864     * Sets the height of panel body
  865     * Used with fixed height (fitHeight:true) docs
  866     * @param {Ext.ux.InfoPanel} panel (defaults to this.expanded)
  867     * @return {Ext.ux.Accordion} this
  868     */
  869   , setPanelHeight: function(panel) {
  870     panel = panel || this.expanded;
  871     if(this.fitHeight && panel && panel.docked) {
  872       panel.body.setHeight(this.getPanelBodyHeight());
  873       panel.setHeight(panel.getHeight());
  874     }
  875     return this;
  876   }
  877   // }}}
  878   // {{{
  879   /**
  880     * Constrains the dragging of panels do the desktop
  881     * @return {Ext.ux.Accordion} this
  882     */
  883   , constrainToDesktop: function() {
  884     this.items.each(function(panel) {
  885       panel.constrainToDesktop();
  886     }, this);
  887     return this;
  888   }
  889   // }}}
  890   // {{{
  891   /**
  892     * Clears dragging constraints of panels
  893     * @return {Ext.ux.Accordion} this
  894     */
  895   , clearConstraints: function() {
  896     this.items.each(function(panel) {
  897       panel.dd.clearConstraints();
  898     });
  899   }
  900   // }}}
  901   // {{{
  902   /**
  903     * Shows all panels
  904     * @param {Boolean} show (optional) if false hides the panels instead of showing
  905     * @param {Boolean} alsoUndocked show also undocked panels (defaults to false)
  906     * @return {Ext.ux.Accordion} this
  907     */
  908   , showAll: function(show, alsoUndocked) {
  909     show = (false === show ? false : true);
  910     this.items.each(function(panel) {
  911       panel.show(show, alsoUndocked);
  912     });
  913     return this;
  914   }
  915   // }}}
  916 
  917   , showOther: function(panel, show, alsoPinned) {
  918     show = (false === show ? false : true);
  919     this.items.each(function(p) {
  920         if(p === panel || (p.pinned && !alsoPinned)) {
  921           return;
  922         }
  923         if(show) {
  924           p.show();
  925         }
  926         else {
  927           p.hide();
  928         }
  929     });
  930   }
  931 
  932   , hideOther: function(panel, alsoPinned) {
  933     this.showOther(panel, false, alsoPinned);
  934   }
  935 
  936   // {{{
  937   /**
  938     * Hides all panels
  939     * @param {Boolean} alsoUndocked hide also undocked panels (defaults to false)
  940     * @return {Ext.ux.Accordion} this
  941     */
  942   , hideAll: function(alsoUndocked) {
  943     return this.showAll(false, alsoUndocked);
  944   }
  945   // }}}
  946   // {{{
  947   /**
  948     * Called internally to disable/enable scrolling of the dock while animating
  949     * @param {Boolean} enable true to enable, false to disable
  950     * @return {void}
  951     * @todo not used at present - revise
  952     */
  953   , setDockScroll: function(enable) {
  954     if(enable && !this.fitHeight) {
  955       this.body.setStyle('overflow','auto');
  956     }
  957     else {
  958       this.body.setStyle('overflow','hidden');
  959     }
  960   }
  961   // }}}
  962   // {{{
  963   /**
  964     * Set Accordion size
  965     * Overrides ContentPanel.setSize
  966     *
  967     * @param {Integer} w width
  968     * @param {Integer} h height
  969     * @return {Ext.ux.Accordion} this
  970     */
  971   , setSize: function(w, h) {
  972     // call parent's setSize
  973     Ext.ux.Accordion.superclass.setSize.call(this, w, h);
  974 //    this.body.setHeight(h);
  975     this.setPanelHeight();
  976 
  977     return this;
  978   }
  979   // }}}
  980   // {{{
  981   /**
  98