/*jslint white: true, nomen: true, maxerr: 50, indent: 4 */
/*global YUI */
/**
* The accordion module creates a control with titles and expandable sections for each
* @module Accordion
*/
YUI.add('accordion', function(Y) {
"use strict";
var ACCORDION = 'Accordion',
ACC_PANEL = 'AccordionPanel',
Lang = Y.Lang,
BBX = 'boundingBox',
BODY = Y.WidgetStdMod.BODY,
HEADER = Y.WidgetStdMod.HEADER,
EXPANDED = 'expanded',
EXPANDING = 'expanding',
COLLAPSED = 'collapsed',
COLLAPSING = 'collapsing',
LABEL = 'label',
CONTENT = 'content',
ICON = 'icon',
STATUS = 'status',
CLOSE = 'close',
PANEL_CLOSE = 'panelClose',
MULTI_EXPAND = 'multiExpand',
RESIZEABLE = 'resizeable',
CLOSEABLE = 'closeable',
UI = 'ui';
/**
* The AccordionPanel class represents one of the panels within an accordion
* @class AccordionPanel
* @extends Widget
* @uses WidgetChild, WidgetStdMod, MakeNode
* @constructor
* @param cfg {object} (optional) configuration attributes
*/
Y[ACC_PANEL] = Y.Base.create(
ACC_PANEL,
Y.Widget,
[Y.WidgetChild,Y.WidgetStdMod,Y.MakeNode],
{
/**
* With the accordion panel container filled with with WidgetStdMod divs
* it seems like too much to have a separate contentBox
* @property CONTENT_TEMPLATE
* @value null
* @protected
*/
CONTENT_TEMPLATE: null,
/**
* This is an override of WidgetStdMod method to prevent it from re-rendering the
* three sections again and again and wiping out the resize handles
* @method _syncUIStdMod
* @protected
*/
_syncUIStdMod : function () {
},
/**
* Adds the resizer to the body of the three section StdMod
* and creates a container for the actual content within
* @method renderUI
* @protected
*/
renderUI: function () {
this.setStdModContent(BODY,this._makeNode());
this.setStdModContent(HEADER, this._makeNode(Y[ACC_PANEL]._HEADER_TEMPLATE));
this._locateNodes();
},
/**
* Listener for the after Expanded change event, toggles the panel
* @method _afterExpanded
* @param ev {EventFacade}
* @private
*/
_uiSetExpanded: function(value, src) {
if (src === UI) {
return;
}
if (value) {
this.expand();
} else {
this.collapse();
}
},
/**
* Sets the label on the header of this panel
* @method _uiSetLabel
* @param value {String} text to be shown
* @private
*/
_uiSetLabel: function (value) {
this._labelNode.setContent(value);
},
/**
* Sets the content on this panel
* @method _uiSetContent
* @param value {String | Node} content to be shown
* @private
*/
_uiSetContent: function (value) {
this._bodyNode.setContent(value);
},
/**
* Plugs/unplugs the resize-plugin (if available) in response to the resizeable attribute
* @method _uiSetResizeable
* @param value {Boolean} new value for the resizeable attribute
* @private
*/
_uiSetResizeable: function (value) {
var body = this.getStdModNode(BODY);
if (Y.Plugin.Resize) {
if (value) {
body.plug(Y.Plugin.Resize, {
handles:['b']
});
} else {
body.unplug(Y.Plugin.Resize);
}
}
},
/**
* Sets the close icon visible or not depending on the attribute closeable
* @method _uiSetCloseable
* @param value {Boolean} new value for the closeeable attribute
* @private
*/
_uiSetCloseable: function (value) {
if (value) {
this._closeNode.show();
} else {
this._closeNode.hide();
}
},
/**
* Expands this panel
* @method expand
*/
expand: function() {
var bbx = this.get(BBX),
cns = this._classNames;
bbx.replaceClass(cns[COLLAPSED],cns[EXPANDING]);
this.getStdModNode(BODY).show(true,{},function() {
bbx.replaceClass(cns[EXPANDING],cns[EXPANDED]);
});
this.set(EXPANDED,true,{src:UI});
},
/**
* Collapses this panel
* @method collapse
*/
collapse: function () {
var bbx = this.get(BBX),
cns = this._classNames;
bbx.replaceClass(cns[EXPANDED],cns[EXPANDING]);
this.getStdModNode(BODY).hide(true,{},function() {
bbx.replaceClass(cns[EXPANDING],cns[COLLAPSED]);
});
this.set(EXPANDED,false,{src:UI});
},
/**
* Toggles this panel from expanded to collapsed
* @method toggle
*/
toggle: function () {
this.set(EXPANDED, !this.get(EXPANDED));
},
/**
* Responds to clicks in the header of this panel to toggle it
* @method _onHeaderClick
* @param ev {EventFacade} uses target to make sure it is the
* header of this accordion and not that of a nested one
* @private
*/
_onHeaderClick: function (ev) {
if (ev.target === this.getStdModNode(HEADER)) {
this.toggle();
}
},
/**
* Convenience method to close this panel.
* Called when the close icon, if present, is clicked.
* It fires the panelClose
event to signal
* the container for it to remove the panel
* from the accordion.
* @method close
*/
/**
* Fired by the panel to signal the accordion to remove this panel.
* Closure of the panel can be prevented by listening and halting this event.
* @event panelClose
*/
close: function () {
this.fire(PANEL_CLOSE);
}
},
{
/**
* Defines the class names used by MakeNode, later stored in this._classNames.
* @property _CLASS_NAMES
* @static
* @protected
*/
_CLASS_NAMES: [EXPANDED, COLLAPSED, BODY, ICON, LABEL, STATUS, CLOSE, EXPANDING, COLLAPSING],
/**
* Defines the template used by MakeNode to build the container for the body
* @property _TEMPLATE
* @static
* @protected
*/
_TEMPLATE: '
panelClose
and destroys the panel
* and removes it from the collection of panels.
* @method
* @param ev {EventFacade} uses ev.target to locate the panel requesting the close
* @private
*/
_afterPanelClose: function (ev) {
var panel = ev.target;
if (this.indexOf(panel) >= 0) {
this.remove(panel);
panel.destroy();
}
},
/**
* Sets the resizeable attribute of all panels to the value set for the whole accordion
* @method _uiSetResizeable
* @param value {Boolean} new value of the resizeable attribute
* @private
*/
_uiSetResizeable: function (value) {
this.each(function (panel) {
panel.set(RESIZEABLE, value);
});
},
/**
* Sets the closeable attribute of all panels to the value set for the whole accordion
* @method _uiSetCloseable
* @param value {Boolean} new value of the closeable attribute
* @private
*/
_uiSetCloseable: function (value) {
this.each(function (panel) {
panel.set(CLOSEABLE, value);
});
}
},
{
/**
* Defines the attributes that MakeNode should link to _uiSetXxxx methods to reflect them in the UI.
* @property _ATTRS_2_UI
* @static
* @protected
*/
_ATTRS_2_UI: {
BIND: [RESIZEABLE, CLOSEABLE],
SYNC: [RESIZEABLE, CLOSEABLE]
},
/**
* Defines events that are to be associated to listeners by MakeNode.
* Here it links a couple of events from the items,
* expandedChange to ensure only one panel is open at a time and
* panelClose to destroy and remove a panel
* @property _EVENTS
* @type Object
* @static
* @protected
*/
_EVENTS: {
THIS: {
'AccordionPanel:expandedChange': '_afterChildExpanded',
'AccordionPanel:panelClose': '_afterPanelClose'
}
},
ATTRS: {
/**
* Default type of children to create. Used by WidgetParent.
* @attribute defaultChildType
* @type WidgetChild
* @default AccordionPanel
* @protected
*/
defaultChildType: {
value: ACC_PANEL
},
/**
* Whether several panels may be expanded at once.
* @attribute multiExpand
* @type Boolean
* @default true
*/
multiExpand: {
value:true,
validator: Lang.isBoolean
},
/**
* Helper attribute to set all panels resizeable attribute at once.
* May be overriden in each panel separately.
* Requires the optional resize-plugin module to be loaded.
* @attribute resizeable
* @type Boolean
* @default true
*/
resizeable: {
value: true,
validator: Lang.isBoolean
},
/**
* Helper attribute to set all panels closeable attribute at once.
* May be overriden in each panel separately.
* @attribute closeable
* @type Boolean
* @default true
*/
closeable: {
value: true,
validator: Lang.isBoolean
}
}
}
);
}, '0.9' ,{
requires:['widget','widget-parent','widget-child','widget-stdmod','makenode'],
optional:['resize-plugin','transition'],
skinnable:true
});