launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #02608
[Merge] lp:~bac/launchpad/add-gallery-accordion into lp:launchpad
Brad Crittenden has proposed merging lp:~bac/launchpad/add-gallery-accordion into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~bac/launchpad/add-gallery-accordion/+merge/49786
= Summary =
Adding the code for an accordion widget and update the tools to include
the JavaScript and assets.
== Proposed fix ==
Created lib/lp/contrib as a repository for this code. The code may
eventually move out of Launchpad into lazr-js.
== Pre-implementation notes ==
Discussions with Deryck and others.
--
https://code.launchpad.net/~bac/launchpad/add-gallery-accordion/+merge/49786
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~bac/launchpad/add-gallery-accordion into lp:launchpad.
=== modified file 'buildout-templates/bin/combine-css.in'
--- buildout-templates/bin/combine-css.in 2011-02-09 21:09:32 +0000
+++ buildout-templates/bin/combine-css.in 2011-02-15 11:27:17 +0000
@@ -35,6 +35,7 @@
'lazr/build/picker/assets/skins/sam/picker.css',
'lazr/build/activator/assets/skins/sam/activator.css',
'lazr/build/choiceedit/assets/choiceedit-core.css',
+ 'yui3-gallery/gallery-accordion/assets/skins/sam/gallery-accordion.css',
# This one goes at the end because it's our main stylesheet and should
# take precedence over the others.
'build/style-3-0.css']
=== added symlink 'lib/canonical/launchpad/icing/yui3-gallery'
=== target is u'../../../lp/contrib/javascript/yui3-gallery'
=== added directory 'lib/lp/contrib'
=== added directory 'lib/lp/contrib/javascript'
=== added directory 'lib/lp/contrib/javascript/yui3-gallery'
=== added directory 'lib/lp/contrib/javascript/yui3-gallery/gallery-accordion'
=== added directory 'lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets'
=== added file 'lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/gallery-accordion-core.css'
--- lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/gallery-accordion-core.css 1970-01-01 00:00:00 +0000
+++ lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/gallery-accordion-core.css 2011-02-15 11:27:17 +0000
@@ -0,0 +1,59 @@
+.yui3-accordion {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ position: relative;
+}
+
+.yui3-accordion-item {
+ position: relative;
+ cursor: pointer;
+ width: 100%;
+}
+
+.yui3-accordion-item .yui3-widget-hd {
+ overflow: hidden;
+}
+
+.yui3-accordion-item .yui3-widget-bd {
+ cursor: default;
+ overflow: hidden;
+ position: relative;
+}
+
+.yui3-accordion-item-icons {
+ position: relative;
+ float: right;
+ overflow: hidden;
+ padding: 1px;
+ height: 25px;
+}
+
+.yui3-accordion-item-icon,
+.yui3-accordion-item-iconexpanded,
+.yui3-accordion-item-iconalwaysvisible,
+.yui3-accordion-item-iconclose {
+ width: 22px;
+ height: 22px;
+}
+
+.yui3-accordion-item-icon,
+.yui3-accordion-item-label {
+ float: left;
+}
+
+.yui3-accordion-item-label{
+ position: relative;
+ top: 4px;
+ _height: 22px;
+}
+
+.yui3-accordion-item-iconexpanded,
+.yui3-accordion-item-iconalwaysvisible,
+.yui3-accordion-item-iconclose {
+ float: left;
+}
+
+.yui3-accordion-item-iconclose-hidden {
+ display: none;
+}
=== added directory 'lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins'
=== added directory 'lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam'
=== added file 'lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam/accordion_sprite.png'
Binary files lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam/accordion_sprite.png 1970-01-01 00:00:00 +0000 and lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam/accordion_sprite.png 2011-02-15 11:27:17 +0000 differ
=== added file 'lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam/gallery-accordion-skin.css'
--- lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam/gallery-accordion-skin.css 1970-01-01 00:00:00 +0000
+++ lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam/gallery-accordion-skin.css 2011-02-15 11:27:17 +0000
@@ -0,0 +1,104 @@
+.yui3-skin-sam .yui3-accordion {
+ border: 1px solid #93B2CC;
+}
+
+.yui3-skin-sam .yui3-accordion-item .yui3-widget-hd {
+ background-image: url( accordion_sprite.png );
+ background-position: 0px 0px;
+ border: 1px solid #93B2CC;
+ height: 25px;
+}
+
+.yui3-skin-sam .yui3-accordion-item-icon,
+.yui3-skin-sam .yui3-accordion-item-iconexpanded,
+.yui3-skin-sam .yui3-accordion-item-iconalwaysvisible,
+.yui3-skin-sam .yui3-accordion-item-iconclose {
+ background-repeat: no-repeat;
+}
+
+.yui3-skin-sam .yui3-accordion-item-icon {
+ background-image: url( accordion_sprite.png );
+ background-position: center -25px;
+ _background-position: center -27px;
+}
+
+.yui3-skin-sam .yui3-accordion-item-label{
+ color: #444444;
+}
+
+.yui3-skin-sam .yui3-accordion-item-label {
+ text-decoration: none;
+ background: transparent;
+ overflow: hidden;
+ color: #444444;
+ font-weight: bold;
+}
+
+.yui3-skin-sam .yui3-accordion-item-label:hover{
+ text-decoration: underline;
+}
+
+.yui3-skin-sam .yui3-accordion-item-iconalwaysvisible,
+.yui3-skin-sam .yui3-accordion-item-iconalwaysvisible-off {
+ background-image: url( accordion_sprite.png );
+ background-position: 0px -85px;
+ _background-position: 0px -87px;
+}
+
+.yui3-skin-sam .yui3-accordion-item-iconalwaysvisible-on {
+ background-image: url( accordion_sprite.png );
+ background-position: 0px -55px;
+ _background-position: 0px -57px;
+}
+
+.yui3-skin-sam .yui3-accordion-item-iconexpanded,
+.yui3-skin-sam .yui3-accordion-item-iconexpanded-off {
+ background-image: url( accordion_sprite.png );
+ background-position: 0px -175px;
+ _background-position: 0px -177px;
+}
+
+.yui3-skin-sam .yui3-accordion-item-iconexpanded-off:hover{
+ background-image: url( accordion_sprite.png );
+ background-position: 0px -205px;
+}
+
+.yui3-skin-sam .yui3-accordion-item-iconexpanded-on {
+ background-image: url( accordion_sprite.png );
+ background-position: 0px -115px;
+ _background-position: 0px -117px;
+}
+
+
+.yui3-skin-sam .yui3-accordion-item-iconexpanded-on:hover {
+ background-image: url( accordion_sprite.png );
+ background-position: 0px -145px;
+}
+
+
+.yui3-skin-sam .yui3-accordion-item-iconexpanded-expanding {
+ background-image: url( wait_expand.gif );
+ background-position: 0px center;
+}
+
+
+.yui3-skin-sam .yui3-accordion-item-iconexpanded-collapsing {
+ background-image: url( wait_collapse.gif );
+ background-position: 0px center;
+}
+
+.yui3-skin-sam .yui3-accordion-item-iconclose {
+ background-image: url( accordion_sprite.png );
+ background-position: 0px -235px;
+ _background-position: 0px -237px;
+}
+
+
+.yui3-skin-sam .yui3-accordion-proxyel-visible {
+ border-color : blue;
+ color : white;
+ font-weight : bold;
+ background-color : red;
+ opacity : 0.7;
+ filter: alpha(opacity = 70);
+}
=== added file 'lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam/gallery-accordion.css'
--- lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam/gallery-accordion.css 1970-01-01 00:00:00 +0000
+++ lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam/gallery-accordion.css 2011-02-15 11:27:17 +0000
@@ -0,0 +1,1 @@
+.yui3-accordion{width:100%;height:100%;overflow:hidden;position:relative;}.yui3-accordion-item{position:relative;cursor:pointer;width:100%;}.yui3-accordion-item .yui3-widget-hd{overflow:hidden;}.yui3-accordion-item .yui3-widget-bd{cursor:default;overflow:hidden;position:relative;}.yui3-accordion-item-icons{position:relative;float:right;overflow:hidden;padding:1px;height:25px;}.yui3-accordion-item-icon,.yui3-accordion-item-iconexpanded,.yui3-accordion-item-iconalwaysvisible,.yui3-accordion-item-iconclose{width:22px;height:22px;}.yui3-accordion-item-icon,.yui3-accordion-item-label{float:left;}.yui3-accordion-item-label{position:relative;top:4px;_height:22px;}.yui3-accordion-item-iconexpanded,.yui3-accordion-item-iconalwaysvisible,.yui3-accordion-item-iconclose{float:left;}.yui3-accordion-item-iconclose-hidden{display:none;}.yui3-skin-sam .yui3-accordion{border:1px solid #93B2CC;}.yui3-skin-sam .yui3-accordion-item .yui3-widget-hd{background-image:url(accordion_sprite.png);background-position:0 0;border:1px solid #93B2CC;height:25px;}.yui3-skin-sam .yui3-accordion-item-icon,.yui3-skin-sam .yui3-accordion-item-iconexpanded,.yui3-skin-sam .yui3-accordion-item-iconalwaysvisible,.yui3-skin-sam .yui3-accordion-item-iconclose{background-repeat:no-repeat;}.yui3-skin-sam .yui3-accordion-item-icon{background-image:url(accordion_sprite.png);background-position:center -25px;_background-position:center -27px;}.yui3-skin-sam .yui3-accordion-item-label{color:#444;}.yui3-skin-sam .yui3-accordion-item-label{text-decoration:none;background:transparent;overflow:hidden;color:#444;font-weight:bold;}.yui3-skin-sam .yui3-accordion-item-label:hover{text-decoration:underline;}.yui3-skin-sam .yui3-accordion-item-iconalwaysvisible,.yui3-skin-sam .yui3-accordion-item-iconalwaysvisible-off{background-image:url(accordion_sprite.png);background-position:0 -85px;_background-position:0 -87px;}.yui3-skin-sam .yui3-accordion-item-iconalwaysvisible-on{background-image:url(accordion_sprite.png);background-position:0 -55px;_background-position:0 -57px;}.yui3-skin-sam .yui3-accordion-item-iconexpanded,.yui3-skin-sam .yui3-accordion-item-iconexpanded-off{background-image:url(accordion_sprite.png);background-position:0 -175px;_background-position:0 -177px;}.yui3-skin-sam .yui3-accordion-item-iconexpanded-off:hover{background-image:url(accordion_sprite.png);background-position:0 -205px;}.yui3-skin-sam .yui3-accordion-item-iconexpanded-on{background-image:url(accordion_sprite.png);background-position:0 -115px;_background-position:0 -117px;}.yui3-skin-sam .yui3-accordion-item-iconexpanded-on:hover{background-image:url(accordion_sprite.png);background-position:0 -145px;}.yui3-skin-sam .yui3-accordion-item-iconexpanded-expanding{background-image:url(wait_expand.gif);background-position:0 center;}.yui3-skin-sam .yui3-accordion-item-iconexpanded-collapsing{background-image:url(wait_collapse.gif);background-position:0 center;}.yui3-skin-sam .yui3-accordion-item-iconclose{background-image:url(accordion_sprite.png);background-position:0 -235px;_background-position:0 -237px;}.yui3-skin-sam .yui3-accordion-proxyel-visible{border-color:blue;color:white;font-weight:bold;background-color:red;opacity:.7;filter:alpha(opacity = 70);}
=== added file 'lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam/wait_collapse.gif'
Binary files lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam/wait_collapse.gif 1970-01-01 00:00:00 +0000 and lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam/wait_collapse.gif 2011-02-15 11:27:17 +0000 differ
=== added file 'lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam/wait_expand.gif'
Binary files lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam/wait_expand.gif 1970-01-01 00:00:00 +0000 and lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/assets/skins/sam/wait_expand.gif 2011-02-15 11:27:17 +0000 differ
=== added file 'lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/gallery-accordion-debug.js'
--- lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/gallery-accordion-debug.js 1970-01-01 00:00:00 +0000
+++ lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/gallery-accordion-debug.js 2011-02-15 11:27:17 +0000
@@ -0,0 +1,2890 @@
+YUI.add('gallery-accordion', function(Y) {
+
+/**
+ * Provides Accordion widget
+ *
+ * @module gallery-accordion
+ */
+
+(function(){
+
+// Local constants
+var Lang = Y.Lang,
+ Node = Y.Node,
+ Anim = Y.Anim,
+ Easing = Y.Easing,
+ AccName = "accordion",
+ WidgetStdMod = Y.WidgetStdMod,
+ QuirksMode = document.compatMode == "BackCompat",
+ IEQuirksMode = QuirksMode && Y.UA.ie > 0,
+ COLLAPSE_HEIGHT = IEQuirksMode ? 1 : 0,
+ getCN = Y.ClassNameManager.getClassName,
+
+ C_ITEM = "yui3-accordion-item",
+ C_PROXY_VISIBLE = getCN( AccName, "proxyel", "visible" ),
+ DRAGGROUP = getCN( AccName, "graggroup" ),
+
+ BEFOREITEMADD = "beforeItemAdd",
+ ITEMADDED = "itemAdded",
+ BEFOREITEMREMOVE = "beforeItemRemove",
+ ITEMREMOVED = "itemRemoved",
+ BEFOREITEMERESIZED = "beforeItemResized",
+ ITEMERESIZED = "itemResized",
+
+ BEFOREITEMEXPAND = "beforeItemExpand",
+ BEFOREITEMCOLLAPSE = "beforeItemCollapse",
+ ITEMEXPANDED = "itemExpanded",
+ ITEMCOLLAPSED = "itemCollapsed",
+
+ BEFOREITEMREORDER = "beforeItemReorder",
+ BEFOREENDITEMREORDER = "beforeEndItemReorder",
+ ITEMREORDERED = "itemReordered",
+
+ DEFAULT = "default",
+ ANIMATION = "animation",
+ ALWAYSVISIBLE = "alwaysVisible",
+ EXPANDED = "expanded",
+ COLLAPSEOTHERSONEXPAND = "collapseOthersOnExpand",
+ ITEMS = "items",
+ CONTENT_HEIGHT = "contentHeight",
+ ICON_CLOSE = "iconClose",
+ ICON_ALWAYSVISIBLE = "iconAlwaysVisible",
+ STRETCH = "stretch",
+ PX = "px",
+ CONTENT_BOX = "contentBox",
+ BOUNDING_BOX = "boundingBox",
+ SRCNODE = "srcNode",
+ RENDERED = "rendered",
+ BODYCONTENT = "bodyContent",
+ CHILDREN = "children",
+ PARENT_NODE = "parentNode",
+ NODE = "node",
+ DATA = "data";
+
+
+/**
+ * Accordion creates an widget, consists of one or more items, which can be collapsed, expanded,
+ * set as always visible and reordered by using Drag&Drop. Collapsing/expanding might be animated.
+ *
+ * @class Accordion
+ * @extends Widget
+ */
+
+Y.Accordion = Y.Base.create( AccName, Y.Widget, [], {
+
+ /**
+ * Signals the beginning of adding an item to the Accordion.
+ *
+ * @event beforeItemAdd
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item being added</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals an item has been added to the Accordion.
+ *
+ * @event itemAdded
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item that has been added</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals the beginning of removing an item.
+ *
+ * @event beforeItemRemove
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item being removed</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals an item has been removed from Accordion.
+ *
+ * @event itemRemoved
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item that has been removed</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals the beginning of resizing an item.
+ *
+ * @event beforeItemResized
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item being resized</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals an item has been resized.
+ *
+ * @event itemResized
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item that has been resized</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals the beginning of expanding an item
+ *
+ * @event beforeItemExpand
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item being expanded</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals the beginning of collapsing an item
+ *
+ * @event beforeItemCollapse
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item being collapsed</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals an item has been expanded
+ *
+ * @event itemExpanded
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item that has been expanded</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals an item has been collapsed
+ *
+ * @event itemCollapsed
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item that has been collapsed</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals the beginning of reordering an item
+ *
+ * @event beforeItemReorder
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item being reordered</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Fires before the end of item reordering
+ *
+ * @event beforeEndItemReorder
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item being reordered</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals an item has been reordered
+ *
+ * @event itemReordered
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item that has been reordered</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Initializer lifecycle implementation for the Accordion class. Publishes events,
+ * initializes internal properties and subscribes for resize event.
+ *
+ * @method initializer
+ * @protected
+ * @param config {Object} Configuration object literal for the Accordion
+ */
+ initializer: function( config ) {
+ this.after( "render", Y.bind( this._afterRender, this ) );
+ },
+
+
+ /**
+ * Destructor lifecycle implementation for the Accordion class.
+ * Removes and destroys all registered items.
+ *
+ * @method destructor
+ * @protected
+ */
+ destructor: function() {
+ var items, item, i, length;
+
+ items = this.get( ITEMS );
+ length = items.length;
+
+ for( i = length - 1; i >= 0; i-- ){
+ item = items[ i ];
+
+ items.splice( i, 1 );
+
+ this._removeItemHandles( item );
+
+ item.destroy();
+ }
+ },
+
+
+ /**
+ * Contains items for collapsing
+ * @property _forCollapsing
+ * @protected
+ * @type Object
+ */
+ _forCollapsing : {},
+
+
+ /**
+ * Contains items for expanding
+ * @property _forExpanding
+ * @protected
+ * @type Object
+ */
+ _forExpanding : {},
+
+
+ /**
+ * Contains currently running animations
+ * @property _animations
+ * @protected
+ * @type Object
+ */
+ _animations : {},
+
+
+ /**
+ * Collection of items handles.
+ * Keeps track of each items's event handle, as returned from <code>Y.on</code> or <code>Y.after</code>.
+ * @property _itemHandles
+ * @private
+ * @type Object
+ */
+ _itemsHandles: {},
+
+
+ /**
+ * Removes all handles, attched to given item
+ *
+ * @method _removeItemHandles
+ * @protected
+ * @param item {Y.AccordionItem} The item, which handles to remove
+ */
+ _removeItemHandles: function( item ){
+ var itemHandles, itemHandle;
+
+ itemHandles = this._itemsHandles[ item ];
+
+ for( itemHandle in itemHandles ){
+ if( itemHandles.hasOwnProperty( itemHandle ) ){
+ itemHandle = itemHandles[ itemHandle ];
+ itemHandle.detach();
+ }
+ }
+
+ delete this._itemsHandles[ item ];
+ },
+
+ /**
+ * Obtains the precise height of the node provided, including padding and border.
+ *
+ * @method _getNodeOffsetHeight
+ * @protected
+ * @param node {Node|HTMLElement} The node to gather the height from
+ * @return {Number} The calculated height or zero in case of failure
+ */
+ _getNodeOffsetHeight: function( node ){
+ var height, preciseRegion;
+
+ if( node instanceof Node ){
+ if( node.hasMethod( "getBoundingClientRect" ) ){
+ preciseRegion = node.invoke( "getBoundingClientRect" );
+
+ if( preciseRegion ){
+ height = preciseRegion.bottom - preciseRegion.top;
+
+ return height;
+ }
+ } else {
+ height = node.get( "offsetHeight" );
+ return Y.Lang.isValue( height ) ? height : 0;
+ }
+ } else if( node ){
+ height = node.offsetHeight;
+ return Y.Lang.isValue( height ) ? height : 0;
+ }
+
+ return 0;
+ },
+
+
+ /**
+ * Updates expand and alwaysVisible properties of given item with the values provided.
+ * The properties will be updated only if needed.
+ *
+ * @method _setItemProperties
+ * @protected
+ * @param item {Y.AccordionItem} The item, which properties should be updated
+ * @param expanding {Boolean} The new value of "expanded" property
+ * @param alwaysVisible {Boolean} The new value of "alwaysVisible" property
+ */
+ _setItemProperties: function( item, expanding, alwaysVisible ){
+ var curAlwaysVisible, curExpanded;
+
+ curAlwaysVisible = item.get( ALWAYSVISIBLE );
+ curExpanded = item.get( EXPANDED );
+
+ if( expanding != curExpanded ){
+ item.set( EXPANDED, expanding, {
+ internalCall: true
+ });
+ }
+
+ if( alwaysVisible !== curAlwaysVisible ){
+ item.set( ALWAYSVISIBLE, alwaysVisible, {
+ internalCall: true
+ });
+ }
+ },
+
+
+ /**
+ * Updates user interface of an item and marks it as expanded, alwaysVisible or both
+ *
+ * @method _setItemUI
+ * @protected
+ * @param item {Y.AccordionItem} The item, which user interface should be updated
+ * @param expanding {Boolean} If true, the item will be marked as expanded.
+ * If false, the item will be marked as collapsed
+ * @param alwaysVisible {Boolean} If true, the item will be marked as always visible.
+ * If false, the always visible mark will be removed
+ */
+ _setItemUI: function( item, expanding, alwaysVisible ){
+ item.markAsExpanded( expanding );
+ item.markAsAlwaysVisible( alwaysVisible );
+ },
+
+
+ /**
+ * Sets listener to resize event
+ *
+ * @method _afterRender
+ * @protected
+ * @param e {Event} after render custom event
+ */
+ _afterRender: function( e ){
+ var resizeEvent;
+
+ resizeEvent = this.get( "resizeEvent" );
+
+ this._setUpResizing( resizeEvent );
+
+ this.after( "resizeEventChange", Y.bind( this._afterResizeEventChange, this ) );
+ },
+
+
+ /**
+ * Set up resizing with the new value provided
+ *
+ * @method _afterResizeEventChange
+ * @protected
+ * @param params {Event} after resizeEventChange custom event
+ */
+ _afterResizeEventChange: function( params ){
+ this._setUpResizing( params.newVal );
+ },
+
+
+ /**
+ * Distributes the involved items as result of user interaction on item header.
+ * Some items might be stored in the list for collapsing, other in the list for expanding.
+ * Finally, invokes <code>_processItems</code> function, except if item has been expanded and
+ * user has clicked on always visible icon.
+ * If the user clicked on close icon, the item will be closed.
+ *
+ * @method _onItemChosen
+ * @protected
+ * @param item {Y.AccordionItem} The item on which user has clicked or pressed key
+ * @param srcIconAlwaysVisible {Boolean} True if the user has clicked on always visible icon
+ * @param srcIconClose {Boolean} True if the user has clicked on close icon
+ */
+ _onItemChosen: function( item, srcIconAlwaysVisible, srcIconClose ){
+ var toBeExcluded, alwaysVisible, expanded, collapseOthersOnExpand;
+
+ toBeExcluded = {};
+ collapseOthersOnExpand = this.get( COLLAPSEOTHERSONEXPAND );
+ alwaysVisible = item.get( ALWAYSVISIBLE );
+ expanded = item.get( EXPANDED );
+
+ if( srcIconClose ){
+ this.removeItem( item );
+ return;
+ } else if( srcIconAlwaysVisible ){
+ if( expanded ){
+ alwaysVisible = !alwaysVisible;
+ expanded = alwaysVisible ? true : expanded;
+
+ this._setItemProperties( item, expanded, alwaysVisible );
+ this._setItemUI( item, expanded, alwaysVisible );
+
+ return;
+ } else {
+ this._forExpanding[ item ] = {
+ 'item': item,
+ alwaysVisible: true
+ };
+
+ if( collapseOthersOnExpand ){
+ toBeExcluded[ item ] = {
+ 'item': item
+ };
+
+ this._storeItemsForCollapsing( toBeExcluded );
+ }
+ }
+ } else {
+ /*
+ * Do the opposite
+ */
+ if( expanded ){
+ this._forCollapsing[ item ] = {
+ 'item': item
+ };
+ } else {
+ this._forExpanding[ item ] = {
+ 'item': item,
+ 'alwaysVisible': alwaysVisible
+ };
+
+ if( collapseOthersOnExpand ){
+ toBeExcluded[ item ] = {
+ 'item': item
+ };
+
+ this._storeItemsForCollapsing( toBeExcluded );
+ }
+ }
+ }
+
+ this._processItems();
+ },
+
+
+ /**
+ * Helper method to adjust the height of all items, which <code>contentHeight</code> property is set as "stretch".
+ * If some item has animation running, it will be stopped before running another one.
+ *
+ * @method adjustStretchItems
+ * @protected
+ * @return {Number} The calculated height per strech item
+ */
+ _adjustStretchItems: function(){
+ var items = this.get( ITEMS ), heightPerStretchItem, forExpanding;
+
+ heightPerStretchItem = this._getHeightPerStretchItem();
+ forExpanding = this._forExpanding;
+
+ Y.Array.each( items, function( item, index, items ){
+ var body, bodyHeight, anim, heightSettings, expanded;
+
+ heightSettings = item.get( CONTENT_HEIGHT );
+ expanded = item.get( EXPANDED );
+
+ if( !forExpanding[ item ] && heightSettings.method === STRETCH && expanded ){
+ anim = this._animations[ item ];
+
+ // stop waiting animation
+ if( anim ){
+ anim.stop();
+ }
+
+ body = item.getStdModNode( WidgetStdMod.BODY );
+ bodyHeight = this._getNodeOffsetHeight( body );
+
+ if( heightPerStretchItem < bodyHeight ){
+ this._processCollapsing( item, heightPerStretchItem );
+ } else if( heightPerStretchItem > bodyHeight ){
+ this._processExpanding( item, heightPerStretchItem );
+ }
+ }
+ }, this );
+
+ return heightPerStretchItem;
+ },
+
+ /**
+ * Calculates the height per strech item.
+ *
+ * @method _getHeightPerStretchItem
+ * @protected
+ * @return {Number} The calculated height per strech item
+ */
+ _getHeightPerStretchItem: function(){
+ var height, items, stretchCounter = 0;
+
+ items = this.get( ITEMS );
+ height = this.get( BOUNDING_BOX ).get( "clientHeight" );
+
+ Y.Array.each( items, function( item, index, items ){
+ var collapsed, itemContentHeight, header, heightSettings, headerHeight;
+
+ header = item.getStdModNode( WidgetStdMod.HEADER );
+ heightSettings = item.get( CONTENT_HEIGHT );
+
+ headerHeight = this._getNodeOffsetHeight( header );
+
+ height -= headerHeight;
+ collapsed = !item.get( EXPANDED );
+
+ if( collapsed ){
+ height -= COLLAPSE_HEIGHT;
+ return;
+ }
+
+ if( heightSettings.method === STRETCH ){
+ stretchCounter++;
+ } else {
+ itemContentHeight = this._getItemContentHeight( item );
+ height -= itemContentHeight;
+ }
+ }, this );
+
+ if( stretchCounter > 0 ){
+ height /= stretchCounter;
+ }
+
+ if( height < 0 ){
+ height = 0;
+ }
+
+ return height;
+ },
+
+
+ /**
+ * Calculates the height of given item depending on its "contentHeight" property.
+ *
+ * @method _getItemContentHeight
+ * @protected
+ * @param item {Y.AccordionItem} The item, which height should be calculated
+ * @return {Number} The calculated item's height
+ */
+ _getItemContentHeight: function( item ){
+ var heightSettings, height = 0, body, bodyContent;
+
+ heightSettings = item.get( CONTENT_HEIGHT );
+
+ if( heightSettings.method === "auto" ){
+ body = item.getStdModNode( WidgetStdMod.BODY );
+ bodyContent = body.get( CHILDREN ).item(0);
+ height = bodyContent ? this._getNodeOffsetHeight( bodyContent ) : 0;
+ } else if( heightSettings.method === "fixed" ) {
+ height = heightSettings.height;
+ } else {
+ height = this._getHeightPerStretchItem();
+ }
+
+ return height;
+ },
+
+
+ /**
+ * Stores all items, which are expanded and not set as always visible in list
+ * in order to be collapsed later.
+ *
+ * @method _storeItemsForCollapsing
+ * @protected
+ * @param itemsToBeExcluded {Object} (optional) Contains one or more <code>Y.AccordionItem</code> instances,
+ * which should be not included in the list
+ */
+ _storeItemsForCollapsing: function( itemsToBeExcluded ){
+ var items;
+
+ itemsToBeExcluded = itemsToBeExcluded || {};
+ items = this.get( ITEMS );
+
+ Y.Array.each( items, function( item, index, items ){
+ var expanded, alwaysVisible;
+
+ expanded = item.get( EXPANDED );
+ alwaysVisible = item.get( ALWAYSVISIBLE );
+
+ if( expanded && !alwaysVisible && !itemsToBeExcluded[ item ] ){
+ this._forCollapsing[ item ] = {
+ 'item': item
+ };
+ }
+ }, this );
+ },
+
+
+ /**
+ * Expands an item to given height. This includes also an update to item's user interface
+ *
+ * @method _expandItem
+ * @protected
+ * @param item {Y.AccordionItem} The item, which should be expanded.
+ * @param height {Number} The height to which we should expand the item
+ */
+ _expandItem: function( item, height ){
+ var alwaysVisible = item.get( ALWAYSVISIBLE );
+
+ this._processExpanding( item, height );
+ this._setItemUI( item, true, alwaysVisible );
+ },
+
+
+ /**
+ * Expands an item to given height. Depending on the <code>useAnimation</code> setting,
+ * the process of expanding might be animated. This setting will be ignored, if <code>forceSkipAnimation</code> param
+ * is <code>true</code>.
+ *
+ * @method _processExpanding
+ * @protected
+ * @param item {Y.AccordionItem} An <code>Y.AccordionItem</code> instance to be expanded
+ * @param forceSkipAnimation {Boolean} If true, the animation will be skipped,
+ * without taking in consideration Accordion's <code>useAnimation</code> setting
+ * @param height {Number} The height to which item should be expanded
+ */
+ _processExpanding: function( item, height, forceSkipAnimation ){
+ var anim, curAnim, animSettings, notifyOthers = false,
+ accAnimationSettings, body;
+
+ body = item.getStdModNode( WidgetStdMod.BODY );
+
+ this.fire( BEFOREITEMERESIZED, {
+ 'item': item
+ });
+
+ if( body.get( "clientHeight" ) <= COLLAPSE_HEIGHT ){
+ notifyOthers = true;
+ this.fire( BEFOREITEMEXPAND, {
+ 'item': item
+ });
+ }
+
+ if( !forceSkipAnimation && this.get( "useAnimation" ) ){
+ animSettings = item.get( ANIMATION ) || {};
+
+ anim = new Anim( {
+ node: body,
+ to: {
+ 'height': height
+ }
+ });
+
+ anim.on( "end", Y.bind( this._onExpandComplete, this, item, notifyOthers ) );
+
+ accAnimationSettings = this.get( ANIMATION );
+
+ anim.set( "duration", animSettings.duration || accAnimationSettings.duration );
+ anim.set( "easing" , animSettings.easing || accAnimationSettings.easing );
+
+ curAnim = this._animations[ item ];
+
+ if( curAnim ){
+ curAnim.stop();
+ }
+
+ item.markAsExpanding( true );
+
+ this._animations[ item ] = anim;
+
+ anim.run();
+ } else {
+ body.setStyle( "height", height + PX );
+
+ this.fire( ITEMERESIZED, {
+ 'item': item
+ });
+
+ if( notifyOthers ){
+ this.fire( ITEMEXPANDED, {
+ 'item': item
+ });
+ }
+ }
+ },
+
+
+ /**
+ * Executes when animated expanding completes
+ *
+ * @method _onExpandComplete
+ * @protected
+ * @param item {Y.AccordionItem} An <code>Y.AccordionItem</code> instance which has been expanded
+ * @param notifyOthers {Boolean} If true, itemExpanded event will be fired
+ */
+ _onExpandComplete: function( item, notifyOthers ){
+ delete this._animations[ item ];
+
+ item.markAsExpanding( false );
+
+ this.fire( ITEMERESIZED, {
+ 'item': item
+ });
+
+ if( notifyOthers ){
+ this.fire( ITEMEXPANDED, {
+ 'item': item
+ });
+ }
+ },
+
+
+ /**
+ * Collapse an item and update its user interface
+ *
+ * @method _collapseItem
+ * @protected
+ * @param item {Y.AccordionItem} The item, which should be collapsed
+ */
+ _collapseItem: function( item ){
+ this._processCollapsing( item, COLLAPSE_HEIGHT );
+ this._setItemUI( item, false, false );
+ },
+
+
+ /**
+ * Collapse an item to given height. Depending on the <code>useAnimation</code> setting,
+ * the process of collapsing might be animated. This setting will be ignored, if <code>forceSkipAnimation</code> param
+ * is <code>true</code>.
+ *
+ * @method _processCollapsing
+ * @protected
+ * @param item {Y.AccordionItem} An <code>Y.AccordionItem</code> instance to be collapsed
+ * @param height {Number} The height to which item should be collapsed
+ * @param forceSkipAnimation {Boolean} If true, the animation will be skipped,
+ * without taking in consideration Accordion's <code>useAnimation</code> setting
+ */
+ _processCollapsing: function( item, height, forceSkipAnimation ){
+ var anim, curAnim, animSettings, accAnimationSettings, body,
+ notifyOthers = (height === COLLAPSE_HEIGHT);
+
+ body = item.getStdModNode( WidgetStdMod.BODY );
+
+
+ this.fire( BEFOREITEMERESIZED, {
+ 'item': item
+ });
+
+ if( notifyOthers ){
+ this.fire( BEFOREITEMCOLLAPSE, {
+ 'item': item
+ });
+ }
+
+ if( !forceSkipAnimation && this.get( "useAnimation" ) ){
+ animSettings = item.get( ANIMATION ) || {};
+
+ anim = new Anim( {
+ node: body,
+ to: {
+ 'height': height
+ }
+ });
+
+ anim.on( "end", Y.bind( this._onCollapseComplete, this, item, notifyOthers ) );
+
+ accAnimationSettings = this.get( ANIMATION );
+
+ anim.set( "duration", animSettings.duration || accAnimationSettings.duration );
+ anim.set( "easing" , animSettings.easing || accAnimationSettings.easing );
+
+ curAnim = this._animations[ item ];
+
+ if( curAnim ){
+ curAnim.stop();
+ }
+
+ item.markAsCollapsing( true );
+
+ this._animations[ item ] = anim;
+
+ anim.run();
+ } else {
+ body.setStyle( "height", height + PX );
+
+ this.fire( ITEMERESIZED, {
+ 'item': item
+ });
+
+ if( notifyOthers ){
+ this.fire( ITEMCOLLAPSED, {
+ 'item': item
+ });
+ }
+ }
+ },
+
+
+ /**
+ * Executes when animated collapsing completes
+ *
+ * @method _onCollapseComplete
+ * @protected
+ * @param item {Y.AccordionItem} An <code>Y.AccordionItem</code> instance which has been collapsed
+ * @param notifyOthers {Boolean} If true, itemCollapsed event will be fired
+ */
+ _onCollapseComplete: function( item, notifyOthers ){
+ delete this._animations[ item ];
+
+ item.markAsCollapsing( false );
+
+ this.fire( ITEMERESIZED, {
+ item: item
+ });
+
+ if( notifyOthers ){
+ this.fire( ITEMCOLLAPSED, {
+ 'item': item
+ });
+ }
+ },
+
+
+ /**
+ * Make an item draggable. The item can be reordered later.
+ *
+ * @method _initItemDragDrop
+ * @protected
+ * @param item {Y.AccordionItem} An <code>Y.AccordionItem</code> instance to be set as draggable
+ */
+ _initItemDragDrop: function( item ){
+ var itemHeader, dd, bb, itemBB, ddrop;
+
+ itemHeader = item.getStdModNode( WidgetStdMod.HEADER );
+
+ if( itemHeader.dd ){
+ return;
+ }
+
+ bb = this.get( BOUNDING_BOX );
+ itemBB = item.get( BOUNDING_BOX );
+
+ dd = new Y.DD.Drag({
+ node: itemHeader,
+ groups: [ DRAGGROUP ]
+ }).plug(Y.Plugin.DDProxy, {
+ moveOnEnd: false
+ }).plug(Y.Plugin.DDConstrained, {
+ constrain2node: bb
+ });
+
+ ddrop = new Y.DD.Drop({
+ node: itemBB,
+ groups: [ DRAGGROUP ]
+ });
+
+ dd.on ( "drag:start", Y.bind( this._onDragStart, this, dd ) );
+ dd.on ( "drag:end" , Y.bind( this._onDragEnd, this, dd ) );
+ dd.after( "drag:end" , Y.bind( this._afterDragEnd, this, dd ) );
+ dd.on ( "drag:drophit", Y.bind( this._onDropHit, this, dd ) );
+ },
+
+
+ /**
+ * Sets the label of the item being dragged on the drag proxy.
+ * Fires beforeItemReorder event - returning false will cancel reordering
+ *
+ * @method _onDragStart
+ * @protected
+ * @param dd {Y.DD.Drag} The drag instance of the item
+ * @param e {Event} the DD instance's drag:start custom event
+ */
+ _onDragStart: function( dd, e ){
+ var dragNode, item;
+
+ item = this.getItem( dd.get( NODE ).get( PARENT_NODE ) );
+ dragNode = dd.get( "dragNode" );
+
+ dragNode.addClass( C_PROXY_VISIBLE );
+ dragNode.set( "innerHTML", item.get( "label" ) );
+
+ return this.fire( BEFOREITEMREORDER, { 'item': item } );
+ },
+
+
+ /**
+ * Restores HTML structure of the drag proxy.
+ * Fires beforeEndItemReorder event - returning false will cancel reordering
+ *
+ * @method _onDragEnd
+ * @protected
+ * @param dd {Y.DD.Drag} The drag instance of the item
+ * @param e {Event} the DD instance's drag:end custom event
+ */
+ _onDragEnd: function( dd, e ){
+ var dragNode, item;
+
+ dragNode = dd.get( "dragNode" );
+
+ dragNode.removeClass( C_PROXY_VISIBLE );
+ dragNode.set( "innerHTML", "" );
+
+ item = this.getItem( dd.get( NODE ).get( PARENT_NODE ) );
+ return this.fire( BEFOREENDITEMREORDER, { 'item': item } );
+ },
+
+
+ /**
+ * Set drophit to false in dragdrop instance's custom value (if there has been drophit) and fires itemReordered event
+ *
+ * @method _afterDragEnd
+ * @protected
+ * @param dd {Y.DD.Drag} The drag instance of the item
+ * @param e {Event} the DD instance's drag:end custom event
+ */
+ _afterDragEnd: function( dd, e ){
+ var item, data;
+
+ data = dd.get( DATA );
+
+ if( data.drophit ){
+ item = this.getItem( dd.get( NODE ).get( PARENT_NODE ) );
+
+ dd.set( DATA, {
+ drophit: false
+ } );
+
+ return this.fire( ITEMREORDERED, { 'item': item } );
+ }
+
+ return true;
+ },
+
+
+ /**
+ * Moves the source item before or after target item.
+ *
+ * @method _onDropHit
+ * @protected
+ * @param dd {Y.DD.Drag} The drag instance of the item
+ * @param e {Event} the DD instance's drag:drophit custom event
+ */
+ _onDropHit: function( dd, e) {
+ var mineIndex, targetItemIndex, targetItemBB, itemBB, cb,
+ goingUp, items, targetItem, item;
+
+ item = this.getItem( dd.get( NODE ).get( PARENT_NODE ) );
+ targetItem = this.getItem( e.drop.get( NODE ) );
+
+ if( targetItem === item ){
+ return false;
+ }
+
+ mineIndex = this.getItemIndex( item );
+ targetItemIndex = this.getItemIndex( targetItem );
+ targetItemBB = targetItem.get( BOUNDING_BOX );
+ itemBB = item.get( BOUNDING_BOX );
+ cb = this.get( CONTENT_BOX );
+ goingUp = false;
+ items = this.get( ITEMS );
+
+ if( targetItemIndex < mineIndex ){
+ goingUp = true;
+ }
+
+ cb.removeChild( itemBB );
+
+ if( goingUp ){
+ cb. insertBefore( itemBB, targetItemBB );
+ items.splice( mineIndex, 1 );
+ items.splice( targetItemIndex, 0, item );
+ } else {
+ cb. insertBefore( itemBB, targetItemBB.next( C_ITEM ) );
+ items.splice( targetItemIndex + 1, 0, item );
+ items.splice( mineIndex, 1 );
+ }
+
+ dd.set( DATA, {
+ drophit: true
+ });
+
+ return true;
+ },
+
+
+ /**
+ * Process items as result of user interaction or properties change.
+ * This includes four steps:
+ * 1. Update the properties of the items
+ * 2. Collapse all items stored in the list for collapsing
+ * 3. Adjust all stretch items
+ * 4. Expand items stored in the list for expanding
+ *
+ * @method _processItems
+ * @protected
+ */
+ _processItems: function(){
+ var forCollapsing, forExpanding, itemCont, heightPerStretchItem,
+ height, heightSettings, item;
+
+ forCollapsing = this._forCollapsing;
+ forExpanding = this._forExpanding;
+
+ this._setItemsProperties();
+
+ for( item in forCollapsing ){
+ if( forCollapsing.hasOwnProperty( item ) ){
+ itemCont = forCollapsing[ item ];
+
+ this._collapseItem( itemCont.item );
+ }
+ }
+
+ heightPerStretchItem = this._adjustStretchItems();
+
+ for( item in forExpanding ){
+ if( forExpanding.hasOwnProperty( item ) ){
+ itemCont = forExpanding[ item ];
+ item = itemCont.item;
+ height = heightPerStretchItem;
+ heightSettings = item.get( CONTENT_HEIGHT );
+
+ if( heightSettings.method !== STRETCH ){
+ height = this._getItemContentHeight( item );
+ }
+
+ this._expandItem( item, height );
+ }
+ }
+
+ this._forCollapsing = {};
+ this._forExpanding = {};
+ },
+
+
+ /**
+ * Update properties of items, which were stored in the lists for collapsing or expanding
+ *
+ * @method _setItemsProperties
+ * @protected
+ */
+ _setItemsProperties: function (){
+ var forCollapsing, forExpanding, itemData;
+
+ forCollapsing = this._forCollapsing;
+ forExpanding = this._forExpanding;
+
+ for( itemData in forCollapsing ){
+ if( forCollapsing.hasOwnProperty( itemData ) ){
+ itemData = forCollapsing[ itemData ];
+ this._setItemProperties( itemData.item, false, false );
+ }
+ }
+
+ for( itemData in forExpanding ){
+ if( forExpanding.hasOwnProperty( itemData ) ){
+ itemData = forExpanding[ itemData ];
+ this._setItemProperties( itemData.item, true, itemData.alwaysVisible );
+ }
+ }
+ },
+
+
+ /**
+ * Handles the change of "expand" property of given item
+ *
+ * @method _afterItemExpand
+ * @protected
+ * @param params {EventFacade} The event facade for the attribute change
+ */
+ _afterItemExpand: function( params ){
+ var expanded, item, alwaysVisible, collapseOthersOnExpand;
+
+ if( params.internalCall ){
+ return;
+ }
+
+ expanded = params.newVal;
+ item = params.currentTarget;
+ alwaysVisible = item.get( ALWAYSVISIBLE );
+ collapseOthersOnExpand = this.get( COLLAPSEOTHERSONEXPAND );
+
+ if( expanded ){
+ this._forExpanding[ item ] = {
+ 'item': item,
+ 'alwaysVisible': alwaysVisible
+ };
+
+ if( collapseOthersOnExpand ){
+ this._storeItemsForCollapsing();
+ }
+ } else {
+ this._forCollapsing[ item ] = {
+ 'item': item
+ };
+ }
+
+ this._processItems();
+ },
+
+ /**
+ * Handles the change of "alwaysVisible" property of given item
+ *
+ * @method _afterItemAlwaysVisible
+ * @protected
+ * @param params {EventFacade} The event facade for the attribute change
+ */
+ _afterItemAlwaysVisible: function( params ){
+ var item, alwaysVisible, expanded;
+
+ if( params.internalCall ){
+ return;
+ }
+
+ alwaysVisible = params.newVal;
+ item = params.currentTarget;
+ expanded = item.get( EXPANDED );
+
+ if( alwaysVisible ){
+ if( expanded ){
+ this._setItemProperties( item, true, true );
+ this._setItemUI( item, true, true );
+ return;
+ } else {
+ this._forExpanding[ item ] = {
+ 'item': item,
+ 'alwaysVisible': true
+ };
+
+ this._storeItemsForCollapsing();
+ }
+ } else {
+ if( expanded ){
+ this._setItemUI( item, true, false );
+ return;
+ } else {
+ return;
+ }
+ }
+
+ this._processItems();
+ },
+
+
+ /**
+ * Handles the change of "contentHeight" property of given item
+ *
+ * @method _afterContentHeight
+ * @protected
+ * @param params {EventFacade} The event facade for the attribute change
+ */
+ _afterContentHeight: function( params ){
+ var item, itemContentHeight, body, bodyHeight, expanded;
+
+ item = params.currentTarget;
+
+ this._adjustStretchItems();
+
+ if( params.newVal.method !== STRETCH ){
+ expanded = item.get( EXPANDED );
+ itemContentHeight = this._getItemContentHeight( item );
+
+ body = item.getStdModNode( WidgetStdMod.BODY );
+ bodyHeight = this._getNodeOffsetHeight( body );
+
+ if( itemContentHeight < bodyHeight ){
+ this._processCollapsing( item, itemContentHeight, !expanded );
+ } else if( itemContentHeight > bodyHeight ){
+ this._processExpanding( item, itemContentHeight, !expanded );
+ }
+ }
+ },
+
+
+ /**
+ * Handles the change of "contentUpdate" property of given item
+ *
+ * @method _afterContentUpdate
+ * @protected
+ * @param params {EventFacade} The event facade for the attribute change
+ */
+ _afterContentUpdate : function( params ){
+ var item, body, bodyHeight, expanded, auto, anim;
+
+ item = params.currentTarget;
+ auto = item.get( "contentHeight" ).method === "auto";
+ expanded = item.get( EXPANDED );
+
+ body = item.getStdModNode( WidgetStdMod.BODY );
+ bodyHeight = this._getNodeOffsetHeight( body );
+
+ if( auto && expanded && params.src !== Y.Widget.UI_SRC ){
+ Y.later( 0, this, function(){
+ var itemContentHeight = this._getItemContentHeight( item );
+
+ if( itemContentHeight !== bodyHeight ){
+ anim = this._animations[ item ];
+
+ // stop waiting animation
+ if( anim ){
+ anim.stop();
+ }
+
+ this._adjustStretchItems();
+
+ if( itemContentHeight < bodyHeight ){
+ this._processCollapsing( item, itemContentHeight, !expanded );
+ } else if( itemContentHeight > bodyHeight ){
+ this._processExpanding( item, itemContentHeight, !expanded );
+ }
+ }
+ } );
+ }
+ },
+
+
+ /**
+ * Subscribe for resize event, which could be provided from the browser or from an arbitrary object.
+ * For example, if there is LayoutManager in the page, it is preferable to subscribe to its resize event,
+ * instead to those, which browser provides.
+ *
+ * @method _setUpResizing
+ * @protected
+ * @param value {String|Object} String "default" or object with the following properties:
+ * <dl>
+ * <dt>sourceObject</dt>
+ * <dd>An abbitrary object</dd>
+ * <dt>resizeEvent</dt>
+ * <dd>The name of its resize event</dd>
+ * </dl>
+ */
+ _setUpResizing: function( value ){
+ if( this._resizeEventHandle ){
+ this._resizeEventHandle.detach();
+ }
+
+ if( value === DEFAULT ){
+ this._resizeEventHandle = Y.on( 'windowresize', Y.bind( this._adjustStretchItems, this ) );
+ } else {
+ this._resizeEventHandle = value.sourceObject.on( value.resizeEvent, Y.bind( this._adjustStretchItems, this ) );
+ }
+ },
+
+
+ /**
+ * Creates one or more items found in Accordion's <code>contentBox</code>
+ *
+ * @method renderUI
+ * @protected
+ */
+ renderUI: function(){
+ var srcNode, itemsDom, contentBox, srcNodeId;
+
+ srcNode = this.get( SRCNODE );
+ contentBox = this.get( CONTENT_BOX );
+ srcNodeId = srcNode.get( "id" );
+
+ /*
+ * Widget 3.1 workaround - the Id of contentBox is generated by YUI, instead to keep srcNode's Id, so we set it manually
+ */
+ contentBox.set( "id", srcNodeId );
+
+ itemsDom = srcNode.all( "> ." + C_ITEM );
+
+ itemsDom.each( function( itemNode, index, itemsDom ){
+ var newItem;
+
+ if( !this.getItem( itemNode ) ){
+ newItem = new Y.AccordionItem({
+ srcNode: itemNode,
+ id : itemNode.get( "id" )
+ });
+
+ this.addItem( newItem );
+ }
+ }, this );
+ },
+
+
+ /**
+ * Add listener to <code>itemChosen</code> event in Accordion's content box
+ *
+ * @method bindUI
+ * @protected
+ */
+ bindUI: function(){
+ var contentBox, itemChosenEvent;
+
+ contentBox = this.get( CONTENT_BOX );
+ itemChosenEvent = this.get( 'itemChosen' );
+
+ contentBox.delegate( itemChosenEvent, Y.bind( this._onItemChosenEvent, this ), '.yui3-widget-hd' );
+ },
+
+
+ /**
+ * Listening for itemChosen event, determines the source (is that iconClose, iconAlwaysVisisble, etc.) and
+ * invokes this._onItemChosen for further processing
+ *
+ * @method _onItemChosenEvent
+ * @protected
+ *
+ * @param e {Event} The itemChosen event
+ */
+ _onItemChosenEvent: function(e){
+ var header, itemNode, item, iconAlwaysVisible,
+ iconClose, srcIconAlwaysVisible, srcIconClose;
+
+ header = e.currentTarget;
+ itemNode = header.get( PARENT_NODE );
+ item = this.getItem( itemNode );
+ iconAlwaysVisible = item.get( ICON_ALWAYSVISIBLE );
+ iconClose = item.get( ICON_CLOSE );
+ srcIconAlwaysVisible = (iconAlwaysVisible === e.target);
+ srcIconClose = (iconClose === e.target);
+
+ this._onItemChosen( item, srcIconAlwaysVisible, srcIconClose );
+ },
+
+
+ /**
+ * Add an item to Accordion. Items could be added/removed multiple times and they
+ * will be rendered in the process of adding, if not.
+ * The item will be expanded, collapsed, or set as always visible depending on the
+ * settings. Item's properties will be also updated, if they are incomplete.
+ * For example, if <code>alwaysVisible</code> is true, but <code>expanded</code>
+ * property is false, it will be set to true also.
+ *
+ * If the second param, <code>parentItem</code> is an <code>Y.AccordionItem</code> instance,
+ * registered in Accordion, the item will be added as child of the <code>parentItem</code>
+ *
+ * @method addItem
+ * @param item {Y.AccordionItem} The item to be added in Accordion
+ * @param parentItem {Y.AccordionItem} (optional) This item will be the parent of the item being added
+ *
+ * @return {Boolean} True in case of successfully added item, false otherwise
+ */
+ addItem: function( item, parentItem ){
+ var expanded, alwaysVisible, itemBody, itemBodyContent, itemIndex, items, contentBox,
+ itemHandles, itemContentBox, res, children;
+
+ res = this.fire( BEFOREITEMADD, {
+ 'item': item
+ });
+
+ if( !res ){
+ return false;
+ }
+
+ items = this.get( ITEMS );
+ contentBox = this.get( CONTENT_BOX );
+
+ itemContentBox = item.get( CONTENT_BOX );
+
+ if( !itemContentBox.inDoc() ){
+ if( parentItem ){
+ itemIndex = this.getItemIndex( parentItem );
+
+ if( itemIndex < 0 ){
+ return false;
+ }
+
+ items.splice( itemIndex, 0, item );
+ contentBox.insertBefore( itemContentBox, parentItem.get( BOUNDING_BOX ) );
+ } else {
+ items.push( item );
+ contentBox.insertBefore( itemContentBox, null );
+ }
+ } else {
+ children = contentBox.get( CHILDREN );
+
+ res = children.some( function( node, index, nodeList ){
+ if( node === itemContentBox ){
+ items.splice( index, 0, item );
+ return true;
+ } else {
+ return false;
+ }
+ }, this );
+
+ if( !res ){
+ return false;
+ }
+ }
+
+ itemBody = item.getStdModNode( WidgetStdMod.BODY );
+ itemBodyContent = item.get( BODYCONTENT );
+
+ if( !itemBody && !itemBodyContent ){
+ item.set( BODYCONTENT, "" );
+ }
+
+ if( !item.get( RENDERED ) ){
+ item.render();
+ }
+
+ expanded = item.get( EXPANDED );
+ alwaysVisible = item.get( ALWAYSVISIBLE );
+
+ expanded = expanded || alwaysVisible;
+
+ if( expanded ){
+ this._forExpanding[ item ] = {
+ 'item': item,
+ 'alwaysVisible': alwaysVisible
+ };
+ } else {
+ this._forCollapsing[ item ] = {
+ 'item': item
+ };
+ }
+
+ this._processItems();
+
+ if( this.get( "reorderItems" ) ){
+ this._initItemDragDrop( item );
+ }
+
+ itemHandles = this._itemsHandles[ item ];
+
+ if( !itemHandles ){
+ itemHandles = {};
+ }
+
+ itemHandles = {
+ "expandedChange" : item.after( "expandedChange", Y.bind( this._afterItemExpand, this ) ),
+ "alwaysVisibleChange" : item.after( "alwaysVisibleChange", Y.bind( this._afterItemAlwaysVisible, this ) ),
+ "contentHeightChange" : item.after( "contentHeightChange", Y.bind( this._afterContentHeight, this ) ),
+ "contentUpdate" : item.after( "contentUpdate", Y.bind( this._afterContentUpdate, this ) )
+ };
+
+ this._itemsHandles[ item ] = itemHandles;
+
+ this.fire( ITEMADDED, {
+ 'item': item
+ });
+
+ return true;
+ },
+
+
+ /**
+ * Removes an previously registered item in Accordion
+ *
+ * @method removeItem
+ * @param p_item {Y.AccordionItem|Number} The item to be removed, or its index
+ * @return {Y.AccordionItem} The removed item or null if not found
+ */
+ removeItem: function( p_item ){
+ var items, bb, item = null, itemIndex;
+
+ items = this.get( ITEMS );
+
+ if( Lang.isNumber( p_item ) ){
+ itemIndex = p_item;
+ } else if( p_item instanceof Y.AccordionItem ){
+ itemIndex = this.getItemIndex( p_item );
+ } else {
+ return null;
+ }
+
+ if( itemIndex >= 0 ){
+
+ this.fire( BEFOREITEMREMOVE, {
+ item: p_item
+ });
+
+ item = items.splice( itemIndex, 1 )[0];
+
+ this._removeItemHandles( item );
+
+ bb = item.get( BOUNDING_BOX );
+ bb.remove();
+
+ this._adjustStretchItems();
+
+ this.fire( ITEMREMOVED, {
+ item: p_item
+ });
+ }
+
+ return item;
+ },
+
+
+ /**
+ * Searching for item, previously registered in Accordion
+ *
+ * @method getItem
+ * @param param {Number|Y.Node} If number, this must be item's index.
+ * If Node, it should be the value of item's <code>contentBox</code> or <code>boundingBox</code> properties
+ *
+ * @return {Y.AccordionItem} The found item or null
+ */
+ getItem: function( param ){
+ var items = this.get( ITEMS ), item = null;
+
+ if( Lang.isNumber( param ) ){
+ item = items[ param ];
+ return (item instanceof Y.AccordionItem) ? item : null;
+ } else if( param instanceof Node ){
+ Y.Array.some( items, function( tmpItem, index, items ){
+ var contentBox = tmpItem.get( CONTENT_BOX );
+
+ /*
+ * Both contentBox and boundingBox point to same node, so it is safe to check only one of them
+ */
+ if( contentBox === param ){
+ item = tmpItem;
+ return true;
+ } else {
+ return false;
+ }
+ }, this );
+ }
+
+ return item;
+ },
+
+
+ /**
+ * Looking for the index of previously registered item
+ *
+ * @method getItemIndex
+ * @param item {Y.AccordionItem} The item which index should be returned
+ * @return {Number} Item index or <code>-1</code> if item has been not found
+ */
+ getItemIndex: function( item ){
+ var res = -1, items;
+
+ if( item instanceof Y.AccordionItem ){
+ items = this.get( ITEMS );
+
+ Y.Array.some( items, function( tmpItem, index, items ){
+ if( tmpItem === item ){
+ res = index;
+ return true;
+ } else {
+ return false;
+ }
+ }, this );
+ }
+
+ return res;
+ },
+
+
+ /**
+ * Overwrites Y.WidgetStdMod fuction in order to resolve Widget 3.1 issue:<br>
+ * If CONTENT_TEMPLATE is null, in renderUI the result of the following code:
+ * <code>this.getStdModNode( Y.WidgetStdMod.HEADER );</code> is null.
+ * The same is with <code>this.getStdModNode( Y.WidgetStdMod.BODY );</code>.
+ *
+ * @method _findStdModSection
+ * @protected
+ * @param {String} section The section for which the render Node is to be found. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @return {Node} The rendered node for the given section, or null if not found.
+ */
+ _findStdModSection: function(section) {
+ return this.get(SRCNODE).one("> ." + Y.WidgetStdMod.SECTION_CLASS_NAMES[section]);
+ },
+
+ CONTENT_TEMPLATE : null
+}, {
+ /**
+ * Static property provides a string to identify the class.
+ *
+ * @property Accordion.NAME
+ * @type String
+ * @static
+ */
+ NAME : AccName,
+
+ /**
+ * Static property used to define the default attribute
+ * configuration for the Accordion.
+ *
+ * @property Accordion.ATTRS
+ * @type Object
+ * @static
+ */
+ ATTRS : {
+ /**
+ * @description The event on which Accordion should listen for user interactions.
+ * The value can be also mousedown or mouseup. Mousedown event can be used if
+ * drag&drop is not enabled
+ *
+ * @attribute itemChosen
+ * @default click
+ * @type String
+ */
+ itemChosen: {
+ value: "click",
+ validator: Lang.isString
+ },
+
+ /**
+ * @description Contains the items, currently added to Accordion
+ *
+ * @attribute items
+ * @readOnly
+ * @default []
+ * @type Array
+ */
+ items: {
+ value: [],
+ readOnly: true,
+ validator: Lang.isArray
+ },
+
+ /**
+ * @attribute resizeEvent
+ *
+ * @description The event on which Accordion should listen for resizing.
+ * The value must be one of these:
+ * <ul>
+ * <li> String "default" - the Accordion will subscribe to Y.windowresize event
+ * </li>
+ * <li> An object in the following form:
+ * {
+ * sourceObject: some_javascript_object,
+ * resizeEvent: an_event_to_subscribe
+ * }
+ * </li>
+ * </ul>
+ * For example, if we are using LayoutManager's instance as sourceObject, we will have to use its "resize" event as resizeEvent
+ *
+ * @default "default"
+ * @type String or Object
+ */
+
+ resizeEvent: {
+ value: DEFAULT,
+ validator: function( value ){
+ if( value === DEFAULT ){
+ return true;
+ } else if( Lang.isObject(value) ){
+ if( Lang.isValue( value.sourceObject ) && Lang.isValue( value.resizeEvent ) ){
+ return true;
+ }
+ }
+
+ return false;
+ }
+ },
+
+ /**
+ * @attribute useAnimation
+ * @description Boolean indicating that Accordion should use animation when expanding or collapsing items.
+ *
+ * @default true
+ * @type Boolean
+ */
+ useAnimation: {
+ value: true,
+ validator: Lang.isBoolean
+ },
+
+ /**
+ * @attribute animation
+ * @description Animation config values, see Y.Animation
+ *
+ * @default <code> {
+ * duration: 1,
+ * easing: Easing.easeOutStrong
+ * }
+ * </code>
+ *
+ * @type Object
+ */
+ animation: {
+ value: {
+ duration: 1,
+ easing: Easing.easeOutStrong
+ },
+ validator: function( value ){
+ return Lang.isObject( value ) && Lang.isNumber( value.duration ) &&
+ Lang.isFunction( value.easing );
+ }
+ },
+
+ /**
+ * @attribute reorderItems
+ * @description Boolean indicating that items can be reordered via drag and drop.<br>
+ *
+ * Enabling items reordering requires also including the optional drag and drop modules in YUI instance:<br>
+ * 'dd-constrain', 'dd-proxy', 'dd-drop', or just 'dd'
+ *
+ * @default false
+ * @type Boolean
+ */
+ reorderItems: {
+ value: false,
+ validator: function(value){
+ return Lang.isBoolean(value) && !Lang.isUndefined( Y.DD );
+ }
+ },
+
+ /**
+ * @attribute collapseOthersOnExpand
+ * @description If true, on item expanding, all other expanded and not set as always visible items, will be collapsed
+ * Otherwise, they will stay open
+ *
+ * @default true
+ * @type Boolean
+ */
+ collapseOthersOnExpand: {
+ value: true,
+ validator: Lang.isBoolean
+ }
+ }
+});
+
+}());
+
+/**
+ * Provides AccordionItem class
+ *
+ * @module gallery-accordion
+ */
+
+(function(){
+
+// Local constants
+var Lang = Y.Lang,
+ Node = Y.Node,
+ JSON = Y.JSON,
+ WidgetStdMod = Y.WidgetStdMod,
+ AccItemName = "accordion-item",
+ getCN = Y.ClassNameManager.getClassName,
+
+ C_ICONEXPANDED_EXPANDING = getCN( AccItemName, "iconexpanded", "expanding" ),
+ C_ICONEXPANDED_COLLAPSING = getCN( AccItemName, "iconexpanded", "collapsing" ),
+
+ C_ICON = getCN( AccItemName, "icon" ),
+ C_LABEL = getCN( AccItemName, "label" ),
+ C_ICONALWAYSVISIBLE = getCN( AccItemName, "iconalwaysvisible" ),
+ C_ICONSCONTAINER = getCN( AccItemName, "icons" ),
+ C_ICONEXPANDED = getCN( AccItemName, "iconexpanded" ),
+ C_ICONCLOSE = getCN( AccItemName, "iconclose" ),
+ C_ICONCLOSE_HIDDEN = getCN( AccItemName, "iconclose", "hidden" ),
+
+ C_ICONEXPANDED_ON = getCN( AccItemName, "iconexpanded", "on" ),
+ C_ICONEXPANDED_OFF = getCN( AccItemName, "iconexpanded", "off" ),
+
+ C_ICONALWAYSVISIBLE_ON = getCN( AccItemName, "iconalwaysvisible", "on" ),
+ C_ICONALWAYSVISIBLE_OFF = getCN( AccItemName, "iconalwaysvisible", "off" ),
+
+ C_EXPANDED = getCN( AccItemName, "expanded" ),
+ C_CLOSABLE = getCN( AccItemName, "closable" ),
+ C_ALWAYSVISIBLE = getCN( AccItemName, "alwaysvisible" ),
+ C_CONTENTHEIGHT = getCN( AccItemName, "contentheight" ),
+
+ TITLE = "title",
+ STRINGS = "strings",
+ RENDERED = "rendered",
+ CLASS_NAME = "className",
+ AUTO = "auto",
+ STRETCH = "stretch",
+ FIXED = "fixed",
+ HEADER_SELECTOR = ".yui3-widget-hd",
+ DOT = ".",
+ HEADER_SELECTOR_SUB = ".yui3-widget-hd " + DOT,
+ INNER_HTML = "innerHTML",
+ ICONS_CONTAINER = "iconsContainer",
+ ICON = "icon",
+ NODE_LABEL = "nodeLabel",
+ ICON_ALWAYSVISIBLE = "iconAlwaysVisible",
+ ICON_EXPANDED = "iconExpanded",
+ ICON_CLOSE = "iconClose",
+ HREF = "href",
+ HREF_VALUE = "#",
+ YUICONFIG = "yuiConfig",
+
+ REGEX_TRUE = /^(?:true|yes|1)$/,
+ REGEX_AUTO = /^auto\s*/,
+ REGEX_STRETCH = /^stretch\s*/,
+ REGEX_FIXED = /^fixed-\d+/;
+
+/**
+ * Create an AccordionItem widget.
+ *
+ * @class AccordionItem
+ * @extends Widget
+ */
+
+Y.AccordionItem = Y.Base.create( AccItemName, Y.Widget, [Y.WidgetStdMod], {
+ /**
+ * Creates the header content
+ *
+ * @method _createHeader
+ * @protected
+ */
+ _createHeader: function(){
+ var closable, templates, strings, iconsContainer,
+ icon, nodeLabel, iconExpanded, iconAlwaysVisible, iconClose;
+
+ icon = this.get( ICON );
+ nodeLabel = this.get( NODE_LABEL );
+ iconExpanded = this.get( ICON_EXPANDED );
+ iconAlwaysVisible = this.get( ICON_ALWAYSVISIBLE );
+ iconClose = this.get( ICON_CLOSE );
+ iconsContainer = this.get( ICONS_CONTAINER );
+
+ strings = this.get( STRINGS );
+ closable = this.get( "closable" );
+ templates = Y.AccordionItem.TEMPLATES;
+
+ if( !icon ){
+ icon = Node.create( templates.icon );
+ this.set( ICON, icon );
+ }
+
+ if( !nodeLabel ){
+ nodeLabel = Node.create( templates.label );
+ this.set( NODE_LABEL, nodeLabel );
+ } else if( !nodeLabel.hasAttribute( HREF ) ){
+ nodeLabel.setAttribute( HREF, HREF_VALUE );
+ }
+
+ nodeLabel.setContent( this.get( "label" ) );
+
+
+ if( !iconsContainer ){
+ iconsContainer = Node.create( templates.iconsContainer );
+ this.set( ICONS_CONTAINER, iconsContainer );
+ }
+
+ if( !iconAlwaysVisible ){
+ iconAlwaysVisible = Node.create( templates.iconAlwaysVisible );
+ iconAlwaysVisible.setAttribute( TITLE, strings.title_always_visible_off );
+ this.set( ICON_ALWAYSVISIBLE, iconAlwaysVisible );
+ } else if( !iconAlwaysVisible.hasAttribute( HREF ) ){
+ iconAlwaysVisible.setAttribute( HREF, HREF_VALUE );
+ }
+
+
+ if( !iconExpanded ){
+ iconExpanded = Node.create( templates.iconExpanded );
+ iconExpanded.setAttribute( TITLE, strings.title_iconexpanded_off );
+ this.set( ICON_EXPANDED, iconExpanded );
+ } else if( !iconExpanded.hasAttribute( HREF ) ){
+ iconExpanded.setAttribute( HREF, HREF_VALUE );
+ }
+
+
+ if( !iconClose ){
+ iconClose = Node.create( templates.iconClose );
+ iconClose.setAttribute( TITLE, strings.title_iconclose );
+ this.set( ICON_CLOSE, iconClose );
+ } else if( !iconClose.hasAttribute( HREF ) ){
+ iconClose.setAttribute( HREF, HREF_VALUE );
+ }
+
+ if( closable ){
+ iconClose.removeClass( C_ICONCLOSE_HIDDEN );
+ } else {
+ iconClose.addClass( C_ICONCLOSE_HIDDEN );
+ }
+
+ this._addHeaderComponents();
+ },
+
+ /**
+ * Add label and icons in the header. Also, it creates header in if not set from markup
+ *
+ * @method _addHeaderComponents
+ * @protected
+ */
+ _addHeaderComponents: function(){
+ var header, icon, nodeLabel, iconsContainer, iconExpanded,
+ iconAlwaysVisible, iconClose;
+
+ icon = this.get( ICON );
+ nodeLabel = this.get( NODE_LABEL );
+ iconExpanded = this.get( ICON_EXPANDED );
+ iconAlwaysVisible = this.get( ICON_ALWAYSVISIBLE );
+ iconClose = this.get( ICON_CLOSE );
+ iconsContainer = this.get( ICONS_CONTAINER );
+
+ header = this.getStdModNode( WidgetStdMod.HEADER );
+
+ if( !header ){
+ header = new Node( document.createDocumentFragment() );
+ header.appendChild( icon );
+ header.appendChild( nodeLabel );
+ header.appendChild( iconsContainer );
+ iconsContainer.appendChild( iconAlwaysVisible );
+ iconsContainer.appendChild( iconExpanded );
+ iconsContainer.appendChild( iconClose );
+
+ this.setStdModContent( WidgetStdMod.HEADER, header, WidgetStdMod.REPLACE );
+ } else {
+ if( !header.contains( icon ) ){
+ if( header.contains( nodeLabel ) ){
+ header.insertBefore( icon, nodeLabel );
+ } else {
+ header.appendChild( icon );
+ }
+ }
+
+ if( !header.contains( nodeLabel ) ){
+ header.appendChild( nodeLabel );
+ }
+
+ if( !header.contains( iconsContainer ) ){
+ header.appendChild( iconsContainer );
+ }
+
+ if( !iconsContainer.contains( iconAlwaysVisible ) ){
+ iconsContainer.appendChild( iconAlwaysVisible );
+ }
+
+ if( !iconsContainer.contains( iconExpanded ) ){
+ iconsContainer.appendChild( iconExpanded );
+ }
+
+ if( !iconsContainer.contains( iconClose ) ){
+ iconsContainer.appendChild( iconClose );
+ }
+ }
+ },
+
+
+ /**
+ * Handles the change of "labelChanged" property. Updates item's UI with the label provided
+ *
+ * @method _labelChanged
+ * @protected
+ * @param params {EventFacade} The event facade for the attribute change
+ */
+ _labelChanged: function( params ){
+ var label;
+
+ if( this.get( RENDERED ) ){
+ label = this.get( NODE_LABEL );
+ label.set( INNER_HTML, params.newVal );
+ }
+ },
+
+
+ /**
+ * Handles the change of "closableChanged" property. Hides or shows close icon
+ *
+ * @method _closableChanged
+ * @protected
+ * @param params {EventFacade} The event facade for the attribute change
+ */
+ _closableChanged: function( params ){
+ var iconClose;
+
+ if( this.get( RENDERED ) ){
+ iconClose = this.get( ICON_CLOSE );
+
+ if( params.newVal ){
+ iconClose.removeClass( C_ICONCLOSE_HIDDEN );
+ } else {
+ iconClose.addClass( C_ICONCLOSE_HIDDEN );
+ }
+ }
+ },
+
+
+ /**
+ * Initializer lifecycle implementation for the AccordionItem class.
+ *
+ * @method initializer
+ * @protected
+ * @param config {Object} Configuration object literal for the AccordionItem
+ */
+ initializer: function( config ) {
+ this.after( "labelChange", Y.bind( this._labelChanged, this ) );
+ this.after( "closableChange", Y.bind( this._closableChanged, this ) );
+ },
+
+
+ /**
+ * Destructor lifecycle implementation for the AccordionItem class.
+ *
+ * @method destructor
+ * @protected
+ */
+ destructor : function() {
+ // EMPTY
+ },
+
+
+ /**
+ * Creates AccordionItem's header.
+ *
+ * @method renderUI
+ * @protected
+ */
+ renderUI: function(){
+ this._createHeader();
+ },
+
+ /**
+ * Configures/Sets up listeners to bind Widget State to UI/DOM
+ *
+ * @method bindUI
+ * @protected
+ */
+ bindUI: function(){
+ var contentBox = this.get( "contentBox" );
+
+ contentBox.delegate( "click", Y.bind( this._onLinkClick, this ), HEADER_SELECTOR + ' a' );
+ },
+
+
+
+ /**
+ * Prevent default action on clicking the link in the label
+ *
+ * @method _onLinkClick
+ * @protected
+ *
+ * @param e {Event} The click event
+ */
+ _onLinkClick: function( e ){
+ e.preventDefault();
+ },
+
+ /**
+ * Marks the item as always visible by adding class to always visible icon.
+ * The icon will be updated only if needed.
+ *
+ * @method markAsAlwaysVisible
+ * @param alwaysVisible {Boolean} If true, the item should be marked as always visible.
+ * @return {Boolean} Return true if the icon has been updated, false if there was no need to update
+ */
+ markAsAlwaysVisible: function( alwaysVisible ){
+ var iconAlwaysVisisble, strings;
+
+ iconAlwaysVisisble = this.get( ICON_ALWAYSVISIBLE );
+ strings = this.get( STRINGS );
+
+ if( alwaysVisible ){
+ if( !iconAlwaysVisisble.hasClass( C_ICONALWAYSVISIBLE_ON ) ){
+ iconAlwaysVisisble.replaceClass( C_ICONALWAYSVISIBLE_OFF, C_ICONALWAYSVISIBLE_ON );
+ iconAlwaysVisisble.set( TITLE, strings.title_always_visible_on );
+ return true;
+ }
+ } else {
+ if( iconAlwaysVisisble.hasClass( C_ICONALWAYSVISIBLE_ON ) ){
+ iconAlwaysVisisble.replaceClass( C_ICONALWAYSVISIBLE_ON, C_ICONALWAYSVISIBLE_OFF );
+ iconAlwaysVisisble.set( TITLE, strings.title_always_visible_off );
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+
+ /**
+ * Marks the item as expanded by adding class to expand icon.
+ * The icon will be updated only if needed.
+ *
+ * @method markAsExpanded
+ * @param expanded {Boolean} Boolean indicating that item should be marked as expanded.
+ * @return {Boolean} Return true if the icon has been updated, false if there was no need to update
+ */
+ markAsExpanded: function( expanded ){
+ var strings, iconExpanded;
+
+ iconExpanded = this.get( ICON_EXPANDED );
+ strings = this.get( STRINGS );
+
+ if( expanded ){
+ if( !iconExpanded.hasClass( C_ICONEXPANDED_ON ) ){
+ iconExpanded.replaceClass( C_ICONEXPANDED_OFF, C_ICONEXPANDED_ON );
+ iconExpanded.set( TITLE , strings.title_iconexpanded_on );
+ return true;
+ }
+ } else {
+ if( iconExpanded.hasClass( C_ICONEXPANDED_ON ) ){
+ iconExpanded.replaceClass( C_ICONEXPANDED_ON, C_ICONEXPANDED_OFF );
+ iconExpanded.set( TITLE , strings.title_iconexpanded_off );
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+
+ /**
+ * Marks the item as expanding by adding class to expand icon.
+ * The method will update icon only if needed.
+ *
+ * @method markAsExpanding
+ * @param expanding {Boolean} Boolean indicating that the item should be marked as expanding.
+ * @return {Boolean} Return true if the icon has been updated, false if there was no need to update
+ */
+ markAsExpanding: function( expanding ){
+ var iconExpanded = this.get( ICON_EXPANDED );
+
+ if( expanding ){
+ if( !iconExpanded.hasClass( C_ICONEXPANDED_EXPANDING ) ){
+ iconExpanded.addClass( C_ICONEXPANDED_EXPANDING );
+ return true;
+ }
+ } else {
+ if( iconExpanded.hasClass( C_ICONEXPANDED_EXPANDING ) ){
+ iconExpanded.removeClass( C_ICONEXPANDED_EXPANDING );
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+
+ /**
+ * Marks the item as collapsing by adding class to expand icon.
+ * The method will update icon only if needed.
+ *
+ * @method markAsCollapsing
+ * @param collapsing {Boolean} Boolean indicating that the item should be marked as collapsing.
+ * @return {Boolean} Return true if the icon has been updated, false if there was no need to update
+ */
+ markAsCollapsing: function( collapsing ){
+ var iconExpanded = this.get( ICON_EXPANDED );
+
+ if( collapsing ){
+ if( !iconExpanded.hasClass( C_ICONEXPANDED_COLLAPSING ) ){
+ iconExpanded.addClass( C_ICONEXPANDED_COLLAPSING );
+ return true;
+ }
+ } else {
+ if( iconExpanded.hasClass( C_ICONEXPANDED_COLLAPSING ) ){
+ iconExpanded.removeClass( C_ICONEXPANDED_COLLAPSING );
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+
+ /**
+ * Forces the item to resize as result of direct content manipulation (via 'innerHTML').
+ * This method should be invoked if 'contentHeight' property has been set to 'auto'.
+ *
+ * @method resize
+ */
+ resize : function(){
+ this.fire( "contentUpdate" );
+ },
+
+
+ /**
+ * Parses and returns the value of contentHeight property, if set method "fixed".
+ * The value must be in this format: fixed-X, where X is integer
+ *
+ * @method _extractFixedMethodValue
+ * @param value {String} The value to be parsed
+ * @return {Number} The parsed value or null
+ * @protected
+ */
+ _extractFixedMethodValue: function( value ){
+ var i, length, chr, height = null;
+
+ for( i = 6, length = value.length; i < length; i++ ){ // 6 = "fixed-".length
+ chr = value.charAt(i);
+ chr = parseInt( chr, 10 );
+
+ if( Lang.isNumber( chr ) ){
+ height = (height * 10) + chr;
+ } else {
+ break;
+ }
+ }
+
+ return height;
+ },
+
+
+ /**
+ * Validator applied to the icon attribute. Setting new value is not allowed if Accordion has been rendered.
+ *
+ * @method _validateIcon
+ * @param value {MIXED} the value for the icon attribute
+ * @return {Boolean}
+ * @protected
+ */
+ _validateIcon: function( value ) {
+ return !this.get(RENDERED) || value;
+ },
+
+
+ /**
+ * Validator applied to the nodeLabel attribute. Setting new value is not allowed if Accordion has been rendered.
+ *
+ * @method _validateNodeLabel
+ * @param value {MIXED} the value for the nodeLabel attribute
+ * @return {Boolean}
+ * @protected
+ */
+ _validateNodeLabel: function( value ) {
+ return !this.get(RENDERED) || value;
+ },
+
+
+ /**
+ * Validator applied to the iconsContainer attribute. Setting new value is not allowed if Accordion has been rendered.
+ *
+ * @method _validateIconsContainer
+ * @param value {MIXED} the value for the iconsContainer attribute
+ * @return {Boolean}
+ * @protected
+ */
+ _validateIconsContainer: function( value ) {
+ return !this.get(RENDERED) || value;
+ },
+
+
+ /**
+ * Validator applied to the iconExpanded attribute. Setting new value is not allowed if Accordion has been rendered.
+ *
+ * @method _validateIconExpanded
+ * @param value {MIXED} the value for the iconExpanded attribute
+ * @return {Boolean}
+ * @protected
+ */
+ _validateIconExpanded: function( value ) {
+ return !this.get(RENDERED) || value;
+ },
+
+
+ /**
+ * Validator applied to the iconAlwaysVisible attribute. Setting new value is not allowed if Accordion has been rendered.
+ *
+ * @method _validateIconAlwaysVisible
+ * @param value {MIXED} the value for the iconAlwaysVisible attribute
+ * @return {Boolean}
+ * @protected
+ */
+ _validateIconAlwaysVisible: function( value ) {
+ return !this.get(RENDERED) || value;
+ },
+
+
+ /**
+ * Validator applied to the iconClose attribute. Setting new value is not allowed if Accordion has been rendered.
+ *
+ * @method _validateIconClose
+ * @param value {MIXED} the value for the iconClose attribute
+ * @return {Boolean}
+ * @protected
+ */
+ _validateIconClose: function( value ) {
+ return !this.get(RENDERED) || value;
+ },
+
+
+ /**
+ * Setter applied to the input when updating the icon attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setIcon
+ * @param value {Node|HTMLElement|String} The icon element Node or selector
+ * @return {Node} The Node if found, null otherwise.
+ * @protected
+ */
+ _setIcon: function( value ){
+ return Y.one( value ) || null;
+ },
+
+
+ /**
+ * Setter applied to the input when updating the nodeLabel attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setNodeLabel
+ * @param value {Node|HTMLElement|String} The nodeLabel element Node or selector
+ * @return {Node} The Node if found, null otherwise.
+ * @protected
+ */
+ _setNodeLabel: function( value ){
+ return Y.one( value ) || null;
+ },
+
+
+ /**
+ * Setter applied to the input when updating the iconsContainer attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setIconsContainer
+ * @param value {Node|HTMLElement|String} The iconsContainer element Node or selector
+ * @return {Node} The Node if found, null otherwise.
+ * @protected
+ */
+ _setIconsContainer: function( value ){
+ return Y.one( value ) || null;
+ },
+
+
+ /**
+ * Setter applied to the input when updating the iconExpanded attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setIconExpanded
+ * @param value {Node|HTMLElement|String} The iconExpanded element Node or selector
+ * @return {Node} The Node if found, null otherwise.
+ * @protected
+ */
+ _setIconExpanded: function( value ){
+ return Y.one( value ) || null;
+ },
+
+
+ /**
+ * Setter applied to the input when updating the iconAlwaysVisible attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setIconAlwaysVisible
+ * @param value {Node|HTMLElement|String} The iconAlwaysVisible element Node or selector
+ * @return {Node} The Node if found, null otherwise.
+ * @protected
+ */
+ _setIconAlwaysVisible: function( value ){
+ return Y.one( value ) || null;
+ },
+
+
+ /**
+ * Setter applied to the input when updating the iconClose attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setIconClose
+ * @param value {Node|HTMLElement|String} The iconClose element Node or selector
+ * @return {Node} The Node if found, null otherwise.
+ * @protected
+ */
+ _setIconClose: function( value ){
+ return Y.one( value ) || null;
+ },
+
+
+ /**
+ * Overwrites Widget's _applyParser method in order to parse yuiConfig attribute before entering in HTML_PARSER attributes
+ *
+ * @method _applyParser
+ * @protected
+ * @param config {Object} User configuration object (will be populated with values from Node)
+ */
+ _applyParser : function(config) {
+ var srcNode;
+
+ srcNode = this.get( "srcNode" );
+
+ if( srcNode ){
+ this._parsedYUIConfig = srcNode.getAttribute( YUICONFIG );
+
+ if( this._parsedYUIConfig ){
+ this._parsedYUIConfig = JSON.parse( this._parsedYUIConfig );
+ }
+ }
+
+ Y.AccordionItem.superclass._applyParser.apply( this, arguments );
+
+ delete this._parsedYUIConfig;
+ },
+
+
+ /**
+ * Overwrites Y.WidgetStdMod fuction in order to resolve Widget 3.1 issue:<br>
+ * If CONTENT_TEMPLATE is null, in renderUI the result of the following code:
+ * <code>this.getStdModNode( Y.WidgetStdMod.HEADER );</code> is null.
+ * The same is with <code>this.getStdModNode( Y.WidgetStdMod.BODY );</code>.
+ *
+ * @method _findStdModSection
+ * @protected
+ * @param {String} section The section for which the render Node is to be found. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @return {Node} The rendered node for the given section, or null if not found.
+ */
+ _findStdModSection: function(section) {
+ return this.get("srcNode").one("> ." + Y.WidgetStdMod.SECTION_CLASS_NAMES[section]);
+ },
+
+ CONTENT_TEMPLATE : null
+}, {
+ /**
+ * Static property provides a string to identify the class.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ */
+ NAME : AccItemName,
+
+ /**
+ * Static property used to define the default attribute
+ * configuration for the Accordion.
+ *
+ * @property Accordion.ATTRS
+ * @type Object
+ * @static
+ */
+ ATTRS : {
+
+ /**
+ * @description The Node, representing item's icon
+ *
+ * @attribute icon
+ * @default null
+ * @type Node
+ */
+ icon: {
+ value: null,
+ validator: function( value ){
+ return this._validateIcon( value );
+ },
+ setter : function( value ) {
+ return this._setIcon( value );
+ }
+ },
+
+ /**
+ * @description The label of item
+ *
+ * @attribute label
+ * @default " "
+ * @type String
+ */
+ label: {
+ value: " ",
+ validator: Lang.isString
+ },
+
+ /**
+ * @description The node, which contains item's label
+ *
+ * @attribute nodeLabel
+ * @default null
+ * @type Node
+ */
+ nodeLabel: {
+ value: null,
+ validator: function( value ){
+ return this._validateNodeLabel( value );
+ },
+ setter : function( value ) {
+ return this._setNodeLabel( value );
+ }
+ },
+
+
+ /**
+ * @description The container of iconAlwaysVisible, iconExpanded and iconClose
+ *
+ * @attribute iconsContainer
+ * @default null
+ * @type Node
+ */
+ iconsContainer: {
+ value: null,
+ validator: function( value ){
+ return this._validateIconsContainer( value );
+ },
+ setter : function( value ) {
+ return this._setIconsContainer( value );
+ }
+ },
+
+ /**
+ * @description The Node, representing icon expanded
+ *
+ * @attribute iconExpanded
+ * @default null
+ * @type Node
+ */
+ iconExpanded: {
+ value: null,
+ validator: function( value ){
+ return this._validateIconExpanded( value );
+ },
+ setter : function( value ) {
+ return this._setIconExpanded( value );
+ }
+ },
+
+
+ /**
+ * @description The Node, representing icon always visible
+ *
+ * @attribute iconAlwaysVisible
+ * @default null
+ * @type Node
+ */
+ iconAlwaysVisible: {
+ value: null,
+ validator: function( value ){
+ return this._validateIconAlwaysVisible( value );
+ },
+ setter : function( value ) {
+ return this._setIconAlwaysVisible( value );
+ }
+ },
+
+
+ /**
+ * @description The Node, representing icon close, or null if the item is not closable
+ *
+ * @attribute iconClose
+ * @default null
+ * @type Node
+ */
+ iconClose: {
+ value: null,
+ validator: function( value ){
+ return this._validateIconClose( value );
+ },
+ setter : function( value ) {
+ return this._setIconClose( value );
+ }
+ },
+
+ /**
+ * @description Get/Set expanded status of the item
+ *
+ * @attribute expanded
+ * @default false
+ * @type Boolean
+ */
+ expanded: {
+ value: false,
+ validator: Lang.isBoolean
+ },
+
+ /**
+ * @description Describe the method, which will be used when expanding/collapsing
+ * the item. The value should be an object with at least one property ("method"):
+ * <dl>
+ * <dt>method</dt>
+ * <dd>The method can be one of these: "auto", "fixed" and "stretch"</dd>
+ * <dt>height</dt>
+ * <dd>Must be set only if method's value is "fixed"</dd>
+ * </dl>
+ *
+ * @attribute contentHeight
+ * @default auto
+ * @type Object
+ */
+ contentHeight: {
+ value: {
+ method: AUTO
+ },
+ validator: function( value ){
+ if( Lang.isObject( value ) ){
+ if( value.method === AUTO ){
+ return true;
+ } else if( value.method === STRETCH ){
+ return true;
+ } else if( value.method === FIXED && Lang.isNumber( value.height ) &&
+ value.height >= 0 ){
+ return true;
+ }
+ }
+
+ return false;
+ }
+ },
+
+ /**
+ * @description Get/Set always visible status of the item
+ *
+ * @attribute alwaysVisible
+ * @default false
+ * @type Boolean
+ */
+ alwaysVisible: {
+ value: false,
+ validator: Lang.isBoolean
+ },
+
+
+ /**
+ * @description Get/Set the animaton specific settings. By default there are no any settings.
+ * If set, they will overwrite Accordion's animation settings
+ *
+ * @attribute animation
+ * @default {}
+ * @type Object
+ */
+ animation: {
+ value: {},
+ validator: Lang.isObject
+ },
+
+ /**
+ * @description Provides client side string localization support.
+ *
+ * @attribute strings
+ * @default Object English messages
+ * @type Object
+ */
+ strings: {
+ value: {
+ title_always_visible_off: "Click to set always visible on",
+ title_always_visible_on: "Click to set always visible off",
+ title_iconexpanded_off: "Click to expand",
+ title_iconexpanded_on: "Click to collapse",
+ title_iconclose: "Click to close"
+ }
+ },
+
+ /**
+ * @description Boolean indicating that the item can be closed by user.
+ * If true, there will be placed close icon, otherwise not
+ *
+ * @attribute closable
+ * @default false
+ * @type Boolean
+ */
+ closable: {
+ value: false,
+ validator: Lang.isBoolean
+ }
+ },
+
+
+ /**
+ * Static Object hash used to capture existing markup for progressive
+ * enhancement. Keys correspond to config attribute names and values
+ * are selectors used to inspect the srcNode for an existing node
+ * structure.
+ *
+ * @property HTML_PARSER
+ * @type Object
+ * @protected
+ * @static
+ */
+ HTML_PARSER : {
+
+ icon: HEADER_SELECTOR_SUB + C_ICON,
+
+ label: function( srcNode ){
+ var node, labelSelector, yuiConfig, label;
+
+ yuiConfig = this._parsedYUIConfig;
+
+ if( yuiConfig && Lang.isValue( yuiConfig.label ) ){
+ return yuiConfig.label;
+ }
+
+ label = srcNode.getAttribute( "data-label" );
+
+ if( label ){
+ return label;
+ }
+
+ labelSelector = HEADER_SELECTOR_SUB + C_LABEL;
+ node = srcNode.one( labelSelector );
+
+ return (node) ? node.get( INNER_HTML ) : null;
+ },
+
+ nodeLabel: HEADER_SELECTOR_SUB + C_LABEL,
+
+ iconsContainer: HEADER_SELECTOR_SUB + C_ICONSCONTAINER,
+
+ iconAlwaysVisible: HEADER_SELECTOR_SUB + C_ICONALWAYSVISIBLE,
+
+ iconExpanded: HEADER_SELECTOR_SUB + C_ICONEXPANDED,
+
+ iconClose: HEADER_SELECTOR_SUB + C_ICONCLOSE,
+
+ expanded: function( srcNode ){
+ var yuiConfig, expanded;
+
+ yuiConfig = this._parsedYUIConfig;
+
+ if( yuiConfig && Lang.isBoolean( yuiConfig.expanded ) ){
+ return yuiConfig.expanded;
+ }
+
+ expanded = srcNode.getAttribute( "data-expanded" );
+
+ if( expanded ) {
+ return REGEX_TRUE.test( expanded );
+ }
+
+ return srcNode.hasClass( C_EXPANDED );
+ },
+
+ alwaysVisible: function( srcNode ){
+ var yuiConfig, alwaysVisible;
+
+ yuiConfig = this._parsedYUIConfig;
+
+ if( yuiConfig && Lang.isBoolean( yuiConfig.alwaysVisible ) ){
+ alwaysVisible = yuiConfig.alwaysVisible;
+ } else {
+ alwaysVisible = srcNode.getAttribute( "data-alwaysvisible" );
+
+ if( alwaysVisible ) {
+ alwaysVisible = REGEX_TRUE.test( alwaysVisible );
+ } else {
+ alwaysVisible = srcNode.hasClass( C_ALWAYSVISIBLE );
+ }
+ }
+
+ if( alwaysVisible ){
+ this.set( "expanded", true, {
+ internalCall: true
+ } );
+ }
+
+ return alwaysVisible;
+ },
+
+ closable: function( srcNode ){
+ var yuiConfig, closable;
+
+ yuiConfig = this._parsedYUIConfig;
+
+ if( yuiConfig && Lang.isBoolean( yuiConfig.closable ) ){
+ return yuiConfig.closable;
+ }
+
+ closable = srcNode.getAttribute( "data-closable" );
+
+ if( closable ) {
+ return REGEX_TRUE.test( closable );
+ }
+
+ return srcNode.hasClass( C_CLOSABLE );
+ },
+
+ contentHeight: function( srcNode ){
+ var contentHeightClass, classValue, height = 0, index, yuiConfig,
+ contentHeight;
+
+ yuiConfig = this._parsedYUIConfig;
+
+ if( yuiConfig && yuiConfig.contentHeight ){
+ return yuiConfig.contentHeight;
+ }
+
+ contentHeight = srcNode.getAttribute( "data-contentheight" );
+
+ if( REGEX_AUTO.test( contentHeight ) ){
+ return {
+ method: AUTO
+ };
+ } else if( REGEX_STRETCH.test( contentHeight ) ){
+ return {
+ method: STRETCH
+ };
+ } else if( REGEX_FIXED.test( contentHeight ) ){
+ height = this._extractFixedMethodValue( contentHeight );
+
+ return {
+ method: FIXED,
+ height: height
+ };
+ }
+
+
+ classValue = srcNode.get( CLASS_NAME );
+
+ contentHeightClass = C_CONTENTHEIGHT + '-';
+
+ index = classValue.indexOf( contentHeightClass, 0);
+
+ if( index >= 0 ){
+ index += contentHeightClass.length;
+
+ classValue = classValue.substring( index );
+
+ if( REGEX_AUTO.test( classValue ) ){
+ return {
+ method: AUTO
+ };
+ } else if( REGEX_STRETCH.test( classValue ) ){
+ return {
+ method: STRETCH
+ };
+ } else if( REGEX_FIXED.test( classValue ) ){
+ height = this._extractFixedMethodValue( classValue );
+
+ return {
+ method: FIXED,
+ height: height
+ };
+ }
+ }
+
+ return null;
+ }
+ },
+
+
+ /**
+ * The template HTML strings for each of header components.
+ * e.g.
+ * <pre>
+ * {
+ * icon : '<a class="yui3-accordion-item-icon"></a>',
+ * label: '<a href="#" class="yui3-accordion-item-label"></a>',
+ * iconsContainer: '<div class="yui3-accordion-item-icons"></div>',
+ * iconAlwaysVisible: '<a href="#" class="yui3-accordion-item-iconalwaysvisible"></a>',
+ * iconExpanded: '<a href="#" class="yui3-accordion-item-iconexpanded"></a>',
+ * iconClose: '<a href="#" class="yui3-accordion-item-iconclose yui3-accordion-item-iconclose-hidden"></a>'
+ * }
+ * </pre>
+ * @property WidgetStdMod.TEMPLATES
+ * @type Object
+ */
+ TEMPLATES : {
+ icon : '<a class="' + C_ICON + '"></a>',
+ label: '<a href="#" class="' + C_LABEL + '"></a>',
+ iconsContainer: '<div class="' + C_ICONSCONTAINER + '"></div>',
+ iconExpanded: ['<a href="#" class="', C_ICONEXPANDED, ' ', C_ICONEXPANDED_OFF, '"></a>'].join(''),
+ iconAlwaysVisible: ['<a href="#" class="', C_ICONALWAYSVISIBLE, ' ', C_ICONALWAYSVISIBLE_OFF, '"></a>'].join(''),
+ iconClose: ['<a href="#" class="', C_ICONCLOSE, ' ', C_ICONCLOSE_HIDDEN, '"></a>'].join('')
+ }
+
+});
+
+}());
+
+
+
+}, '@VERSION@' ,{optional:['dd-constrain', 'dd-proxy', 'dd-drop'], requires:['event', 'anim-easing', 'widget', 'widget-stdmod', 'json-parse']});
=== added file 'lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/gallery-accordion-min.js'
--- lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/gallery-accordion-min.js 1970-01-01 00:00:00 +0000
+++ lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/gallery-accordion-min.js 2011-02-15 11:27:17 +0000
@@ -0,0 +1,4 @@
+YUI.add("gallery-accordion",function(A){(function(){var u=A.Lang,V=A.Node,Z=A.Anim,H=A.Easing,N="accordion",X=A.WidgetStdMod,s=document.compatMode=="BackCompat",T=s&&A.UA.ie>0,U=T?1:0,J=A.ClassNameManager.getClassName,W="yui3-accordion-item",f=J(N,"proxyel","visible"),h=J(N,"graggroup"),b="beforeItemAdd",e="itemAdded",D="beforeItemRemove",i="itemRemoved",C="beforeItemResized",p="itemResized",d="beforeItemExpand",g="beforeItemCollapse",K="itemExpanded",m="itemCollapsed",j="beforeItemReorder",R="beforeEndItemReorder",S="itemReordered",k="default",O="animation",P="alwaysVisible",E="expanded",c="collapseOthersOnExpand",Y="items",t="contentHeight",B="iconClose",F="iconAlwaysVisible",G="stretch",r="px",a="contentBox",n="boundingBox",I="srcNode",l="rendered",o="bodyContent",Q="children",L="parentNode",q="node",M="data";A.Accordion=A.Base.create(N,A.Widget,[],{initializer:function(v){this.after("render",A.bind(this._afterRender,this));},destructor:function(){var v,y,w,x;v=this.get(Y);x=v.length;for(w=x-1;w>=0;w--){y=v[w];v.splice(w,1);this._removeItemHandles(y);y.destroy();}},_forCollapsing:{},_forExpanding:{},_animations:{},_itemsHandles:{},_removeItemHandles:function(x){var w,v;w=this._itemsHandles[x];for(v in w){if(w.hasOwnProperty(v)){v=w[v];v.detach();}}delete this._itemsHandles[x];},_getNodeOffsetHeight:function(x){var v,w;if(x instanceof V){if(x.hasMethod("getBoundingClientRect")){w=x.invoke("getBoundingClientRect");if(w){v=w.bottom-w.top;return v;}}else{v=x.get("offsetHeight");return A.Lang.isValue(v)?v:0;}}else{if(x){v=x.offsetHeight;return A.Lang.isValue(v)?v:0;}}return 0;},_setItemProperties:function(x,z,w){var v,y;v=x.get(P);y=x.get(E);if(z!=y){x.set(E,z,{internalCall:true});}if(w!==v){x.set(P,w,{internalCall:true});}},_setItemUI:function(w,x,v){w.markAsExpanded(x);w.markAsAlwaysVisible(v);},_afterRender:function(w){var v;v=this.get("resizeEvent");this._setUpResizing(v);this.after("resizeEventChange",A.bind(this._afterResizeEventChange,this));},_afterResizeEventChange:function(v){this._setUpResizing(v.newVal);},_onItemChosen:function(AA,AB,v){var z,x,y,w;z={};w=this.get(c);x=AA.get(P);y=AA.get(E);if(v){this.removeItem(AA);return;}else{if(AB){if(y){x=!x;y=x?true:y;this._setItemProperties(AA,y,x);this._setItemUI(AA,y,x);return;}else{this._forExpanding[AA]={"item":AA,alwaysVisible:true};if(w){z[AA]={"item":AA};this._storeItemsForCollapsing(z);}}}else{if(y){this._forCollapsing[AA]={"item":AA};}else{this._forExpanding[AA]={"item":AA,"alwaysVisible":x};if(w){z[AA]={"item":AA};this._storeItemsForCollapsing(z);}}}}this._processItems();},_adjustStretchItems:function(){var w=this.get(Y),x,v;x=this._getHeightPerStretchItem();v=this._forExpanding;A.Array.each(w,function(AC,AB,AA){var y,AE,AD,AF,z;AF=AC.get(t);z=AC.get(E);if(!v[AC]&&AF.method===G&&z){AD=this._animations[AC];if(AD){AD.stop();}y=AC.getStdModNode(X.BODY);AE=this._getNodeOffsetHeight(y);if(x<AE){this._processCollapsing(AC,x);}else{if(x>AE){this._processExpanding(AC,x);}}}},this);return x;},_getHeightPerStretchItem:function(){var v,x,w=0;x=this.get(Y);v=this.get(n).get("clientHeight");A.Array.each(x,function(AC,AB,AA){var AD,z,AF,AE,y;AF=AC.getStdModNode(X.HEADER);AE=AC.get(t);y=this._getNodeOffsetHeight(AF);v-=y;AD=!AC.get(E);if(AD){v-=U;return;}if(AE.method===G){w++;}else{z=this._getItemContentHeight(AC);v-=z;}},this);if(w>0){v/=w;}if(v<0){v=0;}return v;},_getItemContentHeight:function(x){var z,w=0,v,y;z=x.get(t);if(z.method==="auto"){v=x.getStdModNode(X.BODY);y=v.get(Q).item(0);w=y?this._getNodeOffsetHeight(y):0;}else{if(z.method==="fixed"){w=z.height;}else{w=this._getHeightPerStretchItem();}}return w;},_storeItemsForCollapsing:function(w){var v;w=w||{};v=this.get(Y);A.Array.each(v,function(AB,AA,z){var y,x;y=AB.get(E);x=AB.get(P);if(y&&!x&&!w[AB]){this._forCollapsing[AB]={"item":AB};}},this);},_expandItem:function(x,v){var w=x.get(P);this._processExpanding(x,v);this._setItemUI(x,true,w);},_processExpanding:function(AC,AB,v){var w,x,z,AD=false,AA,y;y=AC.getStdModNode(X.BODY);this.fire(C,{"item":AC});if(y.get("clientHeight")<=U){AD=true;this.fire(d,{"item":AC});}if(!v&&this.get("useAnimation")){z=AC.get(O)||{};w=new Z({node:y,to:{"height":AB}});w.on("end",A.bind(this._onExpandComplete,this,AC,AD));AA=this.get(O);w.set("duration",z.duration||AA.duration);w.set("easing",z.easing||AA.easing);x=this._animations[AC];if(x){x.stop();}AC.markAsExpanding(true);this._animations[AC]=w;w.run();}else{y.setStyle("height",AB+r);this.fire(p,{"item":AC});if(AD){this.fire(K,{"item":AC});}}},_onExpandComplete:function(v,w){delete this._animations[v];v.markAsExpanding(false);this.fire(p,{"item":v});if(w){this.fire(K,{"item":v});}},_collapseItem:function(v){this._processCollapsing(v,U);this._setItemUI(v,false,false);},_processCollapsing:function(AC,AB,v){var w,x,z,AA,y,AD=(AB===U);y=AC.getStdModNode(X.BODY);this.fire(C,{"item":AC});if(AD){this.fire(g,{"item":AC});}if(!v&&this.get("useAnimation")){z=AC.get(O)||{};w=new Z({node:y,to:{"height":AB}});w.on("end",A.bind(this._onCollapseComplete,this,AC,AD));AA=this.get(O);w.set("duration",z.duration||AA.duration);w.set("easing",z.easing||AA.easing);x=this._animations[AC];if(x){x.stop();}AC.markAsCollapsing(true);this._animations[AC]=w;w.run();}else{y.setStyle("height",AB+r);this.fire(p,{"item":AC});if(AD){this.fire(m,{"item":AC});}}},_onCollapseComplete:function(v,w){delete this._animations[v];v.markAsCollapsing(false);this.fire(p,{item:v});if(w){this.fire(m,{"item":v});}},_initItemDragDrop:function(w){var AA,v,z,x,y;AA=w.getStdModNode(X.HEADER);if(AA.dd){return;}z=this.get(n);x=w.get(n);v=new A.DD.Drag({node:AA,groups:[h]}).plug(A.Plugin.DDProxy,{moveOnEnd:false}).plug(A.Plugin.DDConstrained,{constrain2node:z});y=new A.DD.Drop({node:x,groups:[h]});v.on("drag:start",A.bind(this._onDragStart,this,v));v.on("drag:end",A.bind(this._onDragEnd,this,v));v.after("drag:end",A.bind(this._afterDragEnd,this,v));v.on("drag:drophit",A.bind(this._onDropHit,this,v));},_onDragStart:function(v,y){var x,w;w=this.getItem(v.get(q).get(L));
+x=v.get("dragNode");x.addClass(f);x.set("innerHTML",w.get("label"));return this.fire(j,{"item":w});},_onDragEnd:function(v,y){var x,w;x=v.get("dragNode");x.removeClass(f);x.set("innerHTML","");w=this.getItem(v.get(q).get(L));return this.fire(R,{"item":w});},_afterDragEnd:function(v,y){var w,x;x=v.get(M);if(x.drophit){w=this.getItem(v.get(q).get(L));v.set(M,{drophit:false});return this.fire(S,{"item":w});}return true;},_onDropHit:function(AE,AA){var z,AD,w,AC,y,v,AB,x,AF;AF=this.getItem(AE.get(q).get(L));x=this.getItem(AA.drop.get(q));if(x===AF){return false;}z=this.getItemIndex(AF);AD=this.getItemIndex(x);w=x.get(n);AC=AF.get(n);y=this.get(a);v=false;AB=this.get(Y);if(AD<z){v=true;}y.removeChild(AC);if(v){y.insertBefore(AC,w);AB.splice(z,1);AB.splice(AD,0,AF);}else{y.insertBefore(AC,w.next(W));AB.splice(AD+1,0,AF);AB.splice(z,1);}AE.set(M,{drophit:true});return true;},_processItems:function(){var x,w,y,AA,v,AB,z;x=this._forCollapsing;w=this._forExpanding;this._setItemsProperties();for(z in x){if(x.hasOwnProperty(z)){y=x[z];this._collapseItem(y.item);}}AA=this._adjustStretchItems();for(z in w){if(w.hasOwnProperty(z)){y=w[z];z=y.item;v=AA;AB=z.get(t);if(AB.method!==G){v=this._getItemContentHeight(z);}this._expandItem(z,v);}}this._forCollapsing={};this._forExpanding={};},_setItemsProperties:function(){var x,w,v;x=this._forCollapsing;w=this._forExpanding;for(v in x){if(x.hasOwnProperty(v)){v=x[v];this._setItemProperties(v.item,false,false);}}for(v in w){if(w.hasOwnProperty(v)){v=w[v];this._setItemProperties(v.item,true,v.alwaysVisible);}}},_afterItemExpand:function(z){var x,y,w,v;if(z.internalCall){return;}x=z.newVal;y=z.currentTarget;w=y.get(P);v=this.get(c);if(x){this._forExpanding[y]={"item":y,"alwaysVisible":w};if(v){this._storeItemsForCollapsing();}}else{this._forCollapsing[y]={"item":y};}this._processItems();},_afterItemAlwaysVisible:function(y){var x,v,w;if(y.internalCall){return;}v=y.newVal;x=y.currentTarget;w=x.get(E);if(v){if(w){this._setItemProperties(x,true,true);this._setItemUI(x,true,true);return;}else{this._forExpanding[x]={"item":x,"alwaysVisible":true};this._storeItemsForCollapsing();}}else{if(w){this._setItemUI(x,true,false);return;}else{return;}}this._processItems();},_afterContentHeight:function(AA){var y,w,v,z,x;y=AA.currentTarget;this._adjustStretchItems();if(AA.newVal.method!==G){x=y.get(E);w=this._getItemContentHeight(y);v=y.getStdModNode(X.BODY);z=this._getNodeOffsetHeight(v);if(w<z){this._processCollapsing(y,w,!x);}else{if(w>z){this._processExpanding(y,w,!x);}}}},_afterContentUpdate:function(AA){var x,v,z,w,AB,y;x=AA.currentTarget;AB=x.get("contentHeight").method==="auto";w=x.get(E);v=x.getStdModNode(X.BODY);z=this._getNodeOffsetHeight(v);if(AB&&w&&AA.src!==A.Widget.UI_SRC){A.later(0,this,function(){var AC=this._getItemContentHeight(x);if(AC!==z){y=this._animations[x];if(y){y.stop();}this._adjustStretchItems();if(AC<z){this._processCollapsing(x,AC,!w);}else{if(AC>z){this._processExpanding(x,AC,!w);}}}});}},_setUpResizing:function(v){if(this._resizeEventHandle){this._resizeEventHandle.detach();}if(v===k){this._resizeEventHandle=A.on("windowresize",A.bind(this._adjustStretchItems,this));}else{this._resizeEventHandle=v.sourceObject.on(v.resizeEvent,A.bind(this._adjustStretchItems,this));}},renderUI:function(){var y,x,w,v;y=this.get(I);w=this.get(a);v=y.get("id");w.set("id",v);x=y.all("> ."+W);x.each(function(AC,z,AB){var AA;if(!this.getItem(AC)){AA=new A.AccordionItem({srcNode:AC,id:AC.get("id")});this.addItem(AA);}},this);},bindUI:function(){var v,w;v=this.get(a);w=this.get("itemChosen");v.delegate(w,A.bind(this._onItemChosenEvent,this),".yui3-widget-hd");},_onItemChosenEvent:function(AA){var AC,AB,x,y,w,z,v;AC=AA.currentTarget;AB=AC.get(L);x=this.getItem(AB);y=x.get(F);w=x.get(B);z=(y===AA.target);v=(w===AA.target);this._onItemChosen(x,z,v);},addItem:function(AH,x){var AB,AF,z,AA,v,AE,AD,AG,y,AC,w;AC=this.fire(b,{"item":AH});if(!AC){return false;}AE=this.get(Y);AD=this.get(a);y=AH.get(a);if(!y.inDoc()){if(x){v=this.getItemIndex(x);if(v<0){return false;}AE.splice(v,0,AH);AD.insertBefore(y,x.get(n));}else{AE.push(AH);AD.insertBefore(y,null);}}else{w=AD.get(Q);AC=w.some(function(AK,AJ,AI){if(AK===y){AE.splice(AJ,0,AH);return true;}else{return false;}},this);if(!AC){return false;}}z=AH.getStdModNode(X.BODY);AA=AH.get(o);if(!z&&!AA){AH.set(o,"");}if(!AH.get(l)){AH.render();}AB=AH.get(E);AF=AH.get(P);AB=AB||AF;if(AB){this._forExpanding[AH]={"item":AH,"alwaysVisible":AF};}else{this._forCollapsing[AH]={"item":AH};}this._processItems();if(this.get("reorderItems")){this._initItemDragDrop(AH);}AG=this._itemsHandles[AH];if(!AG){AG={};}AG={"expandedChange":AH.after("expandedChange",A.bind(this._afterItemExpand,this)),"alwaysVisibleChange":AH.after("alwaysVisibleChange",A.bind(this._afterItemAlwaysVisible,this)),"contentHeightChange":AH.after("contentHeightChange",A.bind(this._afterContentHeight,this)),"contentUpdate":AH.after("contentUpdate",A.bind(this._afterContentUpdate,this))};this._itemsHandles[AH]=AG;this.fire(e,{"item":AH});return true;},removeItem:function(w){var v,z,y=null,x;v=this.get(Y);if(u.isNumber(w)){x=w;}else{if(w instanceof A.AccordionItem){x=this.getItemIndex(w);}else{return null;}}if(x>=0){this.fire(D,{item:w});y=v.splice(x,1)[0];this._removeItemHandles(y);z=y.get(n);z.remove();this._adjustStretchItems();this.fire(i,{item:w});}return y;},getItem:function(x){var v=this.get(Y),w=null;if(u.isNumber(x)){w=v[x];return(w instanceof A.AccordionItem)?w:null;}else{if(x instanceof V){A.Array.some(v,function(AB,AA,z){var y=AB.get(a);if(y===x){w=AB;return true;}else{return false;}},this);}}return w;},getItemIndex:function(x){var w=-1,v;if(x instanceof A.AccordionItem){v=this.get(Y);A.Array.some(v,function(AA,z,y){if(AA===x){w=z;return true;}else{return false;}},this);}return w;},_findStdModSection:function(v){return this.get(I).one("> ."+A.WidgetStdMod.SECTION_CLASS_NAMES[v]);},CONTENT_TEMPLATE:null},{NAME:N,ATTRS:{itemChosen:{value:"click",validator:u.isString},items:{value:[],readOnly:true,validator:u.isArray},resizeEvent:{value:k,validator:function(v){if(v===k){return true;
+}else{if(u.isObject(v)){if(u.isValue(v.sourceObject)&&u.isValue(v.resizeEvent)){return true;}}}return false;}},useAnimation:{value:true,validator:u.isBoolean},animation:{value:{duration:1,easing:H.easeOutStrong},validator:function(v){return u.isObject(v)&&u.isNumber(v.duration)&&u.isFunction(v.easing);}},reorderItems:{value:false,validator:function(v){return u.isBoolean(v)&&!u.isUndefined(A.DD);}},collapseOthersOnExpand:{value:true,validator:u.isBoolean}}});}());(function(){var u=A.Lang,b=A.Node,P=A.JSON,c=A.WidgetStdMod,d="accordion-item",N=A.ClassNameManager.getClassName,D=N(d,"iconexpanded","expanding"),s=N(d,"iconexpanded","collapsing"),O=N(d,"icon"),K=N(d,"label"),o=N(d,"iconalwaysvisible"),f=N(d,"icons"),m=N(d,"iconexpanded"),j=N(d,"iconclose"),Q=N(d,"iconclose","hidden"),S=N(d,"iconexpanded","on"),L=N(d,"iconexpanded","off"),E=N(d,"iconalwaysvisible","on"),k=N(d,"iconalwaysvisible","off"),Y=N(d,"expanded"),V=N(d,"closable"),h=N(d,"alwaysvisible"),i=N(d,"contentheight"),l="title",C="strings",q="rendered",H="className",e="auto",J="stretch",W="fixed",T=".yui3-widget-hd",Z=".",n=".yui3-widget-hd "+Z,r="innerHTML",t="iconsContainer",g="icon",R="nodeLabel",G="iconAlwaysVisible",a="iconExpanded",B="iconClose",p="href",U="#",F="yuiConfig",I=/^(?:true|yes|1)$/,M=/^auto\s*/,v=/^stretch\s*/,X=/^fixed-\d+/;A.AccordionItem=A.Base.create(d,A.Widget,[A.WidgetStdMod],{_createHeader:function(){var AE,AC,AD,AA,AB,z,x,w,y;AB=this.get(g);z=this.get(R);x=this.get(a);w=this.get(G);y=this.get(B);AA=this.get(t);AD=this.get(C);AE=this.get("closable");AC=A.AccordionItem.TEMPLATES;if(!AB){AB=b.create(AC.icon);this.set(g,AB);}if(!z){z=b.create(AC.label);this.set(R,z);}else{if(!z.hasAttribute(p)){z.setAttribute(p,U);}}z.setContent(this.get("label"));if(!AA){AA=b.create(AC.iconsContainer);this.set(t,AA);}if(!w){w=b.create(AC.iconAlwaysVisible);w.setAttribute(l,AD.title_always_visible_off);this.set(G,w);}else{if(!w.hasAttribute(p)){w.setAttribute(p,U);}}if(!x){x=b.create(AC.iconExpanded);x.setAttribute(l,AD.title_iconexpanded_off);this.set(a,x);}else{if(!x.hasAttribute(p)){x.setAttribute(p,U);}}if(!y){y=b.create(AC.iconClose);y.setAttribute(l,AD.title_iconclose);this.set(B,y);}else{if(!y.hasAttribute(p)){y.setAttribute(p,U);}}if(AE){y.removeClass(Q);}else{y.addClass(Q);}this._addHeaderComponents();},_addHeaderComponents:function(){var AC,x,AB,y,AA,z,w;x=this.get(g);AB=this.get(R);AA=this.get(a);z=this.get(G);w=this.get(B);y=this.get(t);AC=this.getStdModNode(c.HEADER);if(!AC){AC=new b(document.createDocumentFragment());AC.appendChild(x);AC.appendChild(AB);AC.appendChild(y);y.appendChild(z);y.appendChild(AA);y.appendChild(w);this.setStdModContent(c.HEADER,AC,c.REPLACE);}else{if(!AC.contains(x)){if(AC.contains(AB)){AC.insertBefore(x,AB);}else{AC.appendChild(x);}}if(!AC.contains(AB)){AC.appendChild(AB);}if(!AC.contains(y)){AC.appendChild(y);}if(!y.contains(z)){y.appendChild(z);}if(!y.contains(AA)){y.appendChild(AA);}if(!y.contains(w)){y.appendChild(w);}}},_labelChanged:function(x){var w;if(this.get(q)){w=this.get(R);w.set(r,x.newVal);}},_closableChanged:function(x){var w;if(this.get(q)){w=this.get(B);if(x.newVal){w.removeClass(Q);}else{w.addClass(Q);}}},initializer:function(w){this.after("labelChange",A.bind(this._labelChanged,this));this.after("closableChange",A.bind(this._closableChanged,this));},destructor:function(){},renderUI:function(){this._createHeader();},bindUI:function(){var w=this.get("contentBox");w.delegate("click",A.bind(this._onLinkClick,this),T+" a");},_onLinkClick:function(w){w.preventDefault();},markAsAlwaysVisible:function(x){var y,w;y=this.get(G);w=this.get(C);if(x){if(!y.hasClass(E)){y.replaceClass(k,E);y.set(l,w.title_always_visible_on);return true;}}else{if(y.hasClass(E)){y.replaceClass(E,k);y.set(l,w.title_always_visible_off);return true;}}return false;},markAsExpanded:function(x){var w,y;y=this.get(a);w=this.get(C);if(x){if(!y.hasClass(S)){y.replaceClass(L,S);y.set(l,w.title_iconexpanded_on);return true;}}else{if(y.hasClass(S)){y.replaceClass(S,L);y.set(l,w.title_iconexpanded_off);return true;}}return false;},markAsExpanding:function(x){var w=this.get(a);if(x){if(!w.hasClass(D)){w.addClass(D);return true;}}else{if(w.hasClass(D)){w.removeClass(D);return true;}}return false;},markAsCollapsing:function(w){var x=this.get(a);if(w){if(!x.hasClass(s)){x.addClass(s);return true;}}else{if(x.hasClass(s)){x.removeClass(s);return true;}}return false;},resize:function(){this.fire("contentUpdate");},_extractFixedMethodValue:function(AA){var x,z,y,w=null;for(x=6,z=AA.length;x<z;x++){y=AA.charAt(x);y=parseInt(y,10);if(u.isNumber(y)){w=(w*10)+y;}else{break;}}return w;},_validateIcon:function(w){return !this.get(q)||w;},_validateNodeLabel:function(w){return !this.get(q)||w;},_validateIconsContainer:function(w){return !this.get(q)||w;},_validateIconExpanded:function(w){return !this.get(q)||w;},_validateIconAlwaysVisible:function(w){return !this.get(q)||w;},_validateIconClose:function(w){return !this.get(q)||w;},_setIcon:function(w){return A.one(w)||null;},_setNodeLabel:function(w){return A.one(w)||null;},_setIconsContainer:function(w){return A.one(w)||null;},_setIconExpanded:function(w){return A.one(w)||null;},_setIconAlwaysVisible:function(w){return A.one(w)||null;},_setIconClose:function(w){return A.one(w)||null;},_applyParser:function(w){var x;x=this.get("srcNode");if(x){this._parsedYUIConfig=x.getAttribute(F);if(this._parsedYUIConfig){this._parsedYUIConfig=P.parse(this._parsedYUIConfig);}}A.AccordionItem.superclass._applyParser.apply(this,arguments);delete this._parsedYUIConfig;},_findStdModSection:function(w){return this.get("srcNode").one("> ."+A.WidgetStdMod.SECTION_CLASS_NAMES[w]);},CONTENT_TEMPLATE:null},{NAME:d,ATTRS:{icon:{value:null,validator:function(w){return this._validateIcon(w);},setter:function(w){return this._setIcon(w);}},label:{value:" ",validator:u.isString},nodeLabel:{value:null,validator:function(w){return this._validateNodeLabel(w);},setter:function(w){return this._setNodeLabel(w);
+}},iconsContainer:{value:null,validator:function(w){return this._validateIconsContainer(w);},setter:function(w){return this._setIconsContainer(w);}},iconExpanded:{value:null,validator:function(w){return this._validateIconExpanded(w);},setter:function(w){return this._setIconExpanded(w);}},iconAlwaysVisible:{value:null,validator:function(w){return this._validateIconAlwaysVisible(w);},setter:function(w){return this._setIconAlwaysVisible(w);}},iconClose:{value:null,validator:function(w){return this._validateIconClose(w);},setter:function(w){return this._setIconClose(w);}},expanded:{value:false,validator:u.isBoolean},contentHeight:{value:{method:e},validator:function(w){if(u.isObject(w)){if(w.method===e){return true;}else{if(w.method===J){return true;}else{if(w.method===W&&u.isNumber(w.height)&&w.height>=0){return true;}}}}return false;}},alwaysVisible:{value:false,validator:u.isBoolean},animation:{value:{},validator:u.isObject},strings:{value:{title_always_visible_off:"Click to set always visible on",title_always_visible_on:"Click to set always visible off",title_iconexpanded_off:"Click to expand",title_iconexpanded_on:"Click to collapse",title_iconclose:"Click to close"}},closable:{value:false,validator:u.isBoolean}},HTML_PARSER:{icon:n+O,label:function(z){var y,AA,x,w;x=this._parsedYUIConfig;if(x&&u.isValue(x.label)){return x.label;}w=z.getAttribute("data-label");if(w){return w;}AA=n+K;y=z.one(AA);return(y)?y.get(r):null;},nodeLabel:n+K,iconsContainer:n+f,iconAlwaysVisible:n+o,iconExpanded:n+m,iconClose:n+j,expanded:function(y){var x,w;x=this._parsedYUIConfig;if(x&&u.isBoolean(x.expanded)){return x.expanded;}w=y.getAttribute("data-expanded");if(w){return I.test(w);}return y.hasClass(Y);},alwaysVisible:function(y){var x,w;x=this._parsedYUIConfig;if(x&&u.isBoolean(x.alwaysVisible)){w=x.alwaysVisible;}else{w=y.getAttribute("data-alwaysvisible");if(w){w=I.test(w);}else{w=y.hasClass(h);}}if(w){this.set("expanded",true,{internalCall:true});}return w;},closable:function(y){var x,w;x=this._parsedYUIConfig;if(x&&u.isBoolean(x.closable)){return x.closable;}w=y.getAttribute("data-closable");if(w){return I.test(w);}return y.hasClass(V);},contentHeight:function(AC){var AA,AB,w=0,x,z,y;z=this._parsedYUIConfig;if(z&&z.contentHeight){return z.contentHeight;}y=AC.getAttribute("data-contentheight");if(M.test(y)){return{method:e};}else{if(v.test(y)){return{method:J};}else{if(X.test(y)){w=this._extractFixedMethodValue(y);return{method:W,height:w};}}}AB=AC.get(H);AA=i+"-";x=AB.indexOf(AA,0);if(x>=0){x+=AA.length;AB=AB.substring(x);if(M.test(AB)){return{method:e};}else{if(v.test(AB)){return{method:J};}else{if(X.test(AB)){w=this._extractFixedMethodValue(AB);return{method:W,height:w};}}}}return null;}},TEMPLATES:{icon:'<a class="'+O+'"></a>',label:'<a href="#" class="'+K+'"></a>',iconsContainer:'<div class="'+f+'"></div>',iconExpanded:['<a href="#" class="',m," ",L,'"></a>'].join(""),iconAlwaysVisible:['<a href="#" class="',o," ",k,'"></a>'].join(""),iconClose:['<a href="#" class="',j," ",Q,'"></a>'].join("")}});}());},"@VERSION@",{optional:["dd-constrain","dd-proxy","dd-drop"],requires:["event","anim-easing","widget","widget-stdmod","json-parse"]});
\ No newline at end of file
=== added file 'lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/gallery-accordion.js'
--- lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/gallery-accordion.js 1970-01-01 00:00:00 +0000
+++ lib/lp/contrib/javascript/yui3-gallery/gallery-accordion/gallery-accordion.js 2011-02-15 11:27:17 +0000
@@ -0,0 +1,2890 @@
+YUI.add('gallery-accordion', function(Y) {
+
+/**
+ * Provides Accordion widget
+ *
+ * @module gallery-accordion
+ */
+
+(function(){
+
+// Local constants
+var Lang = Y.Lang,
+ Node = Y.Node,
+ Anim = Y.Anim,
+ Easing = Y.Easing,
+ AccName = "accordion",
+ WidgetStdMod = Y.WidgetStdMod,
+ QuirksMode = document.compatMode == "BackCompat",
+ IEQuirksMode = QuirksMode && Y.UA.ie > 0,
+ COLLAPSE_HEIGHT = IEQuirksMode ? 1 : 0,
+ getCN = Y.ClassNameManager.getClassName,
+
+ C_ITEM = "yui3-accordion-item",
+ C_PROXY_VISIBLE = getCN( AccName, "proxyel", "visible" ),
+ DRAGGROUP = getCN( AccName, "graggroup" ),
+
+ BEFOREITEMADD = "beforeItemAdd",
+ ITEMADDED = "itemAdded",
+ BEFOREITEMREMOVE = "beforeItemRemove",
+ ITEMREMOVED = "itemRemoved",
+ BEFOREITEMERESIZED = "beforeItemResized",
+ ITEMERESIZED = "itemResized",
+
+ BEFOREITEMEXPAND = "beforeItemExpand",
+ BEFOREITEMCOLLAPSE = "beforeItemCollapse",
+ ITEMEXPANDED = "itemExpanded",
+ ITEMCOLLAPSED = "itemCollapsed",
+
+ BEFOREITEMREORDER = "beforeItemReorder",
+ BEFOREENDITEMREORDER = "beforeEndItemReorder",
+ ITEMREORDERED = "itemReordered",
+
+ DEFAULT = "default",
+ ANIMATION = "animation",
+ ALWAYSVISIBLE = "alwaysVisible",
+ EXPANDED = "expanded",
+ COLLAPSEOTHERSONEXPAND = "collapseOthersOnExpand",
+ ITEMS = "items",
+ CONTENT_HEIGHT = "contentHeight",
+ ICON_CLOSE = "iconClose",
+ ICON_ALWAYSVISIBLE = "iconAlwaysVisible",
+ STRETCH = "stretch",
+ PX = "px",
+ CONTENT_BOX = "contentBox",
+ BOUNDING_BOX = "boundingBox",
+ SRCNODE = "srcNode",
+ RENDERED = "rendered",
+ BODYCONTENT = "bodyContent",
+ CHILDREN = "children",
+ PARENT_NODE = "parentNode",
+ NODE = "node",
+ DATA = "data";
+
+
+/**
+ * Accordion creates an widget, consists of one or more items, which can be collapsed, expanded,
+ * set as always visible and reordered by using Drag&Drop. Collapsing/expanding might be animated.
+ *
+ * @class Accordion
+ * @extends Widget
+ */
+
+Y.Accordion = Y.Base.create( AccName, Y.Widget, [], {
+
+ /**
+ * Signals the beginning of adding an item to the Accordion.
+ *
+ * @event beforeItemAdd
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item being added</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals an item has been added to the Accordion.
+ *
+ * @event itemAdded
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item that has been added</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals the beginning of removing an item.
+ *
+ * @event beforeItemRemove
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item being removed</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals an item has been removed from Accordion.
+ *
+ * @event itemRemoved
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item that has been removed</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals the beginning of resizing an item.
+ *
+ * @event beforeItemResized
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item being resized</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals an item has been resized.
+ *
+ * @event itemResized
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item that has been resized</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals the beginning of expanding an item
+ *
+ * @event beforeItemExpand
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item being expanded</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals the beginning of collapsing an item
+ *
+ * @event beforeItemCollapse
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item being collapsed</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals an item has been expanded
+ *
+ * @event itemExpanded
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item that has been expanded</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals an item has been collapsed
+ *
+ * @event itemCollapsed
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item that has been collapsed</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals the beginning of reordering an item
+ *
+ * @event beforeItemReorder
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item being reordered</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Fires before the end of item reordering
+ *
+ * @event beforeEndItemReorder
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item being reordered</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Signals an item has been reordered
+ *
+ * @event itemReordered
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>item</dt>
+ * <dd>An <code>AccordionItem</code> instance of the item that has been reordered</dd>
+ * </dl>
+ */
+
+
+ /**
+ * Initializer lifecycle implementation for the Accordion class. Publishes events,
+ * initializes internal properties and subscribes for resize event.
+ *
+ * @method initializer
+ * @protected
+ * @param config {Object} Configuration object literal for the Accordion
+ */
+ initializer: function( config ) {
+ this.after( "render", Y.bind( this._afterRender, this ) );
+ },
+
+
+ /**
+ * Destructor lifecycle implementation for the Accordion class.
+ * Removes and destroys all registered items.
+ *
+ * @method destructor
+ * @protected
+ */
+ destructor: function() {
+ var items, item, i, length;
+
+ items = this.get( ITEMS );
+ length = items.length;
+
+ for( i = length - 1; i >= 0; i-- ){
+ item = items[ i ];
+
+ items.splice( i, 1 );
+
+ this._removeItemHandles( item );
+
+ item.destroy();
+ }
+ },
+
+
+ /**
+ * Contains items for collapsing
+ * @property _forCollapsing
+ * @protected
+ * @type Object
+ */
+ _forCollapsing : {},
+
+
+ /**
+ * Contains items for expanding
+ * @property _forExpanding
+ * @protected
+ * @type Object
+ */
+ _forExpanding : {},
+
+
+ /**
+ * Contains currently running animations
+ * @property _animations
+ * @protected
+ * @type Object
+ */
+ _animations : {},
+
+
+ /**
+ * Collection of items handles.
+ * Keeps track of each items's event handle, as returned from <code>Y.on</code> or <code>Y.after</code>.
+ * @property _itemHandles
+ * @private
+ * @type Object
+ */
+ _itemsHandles: {},
+
+
+ /**
+ * Removes all handles, attched to given item
+ *
+ * @method _removeItemHandles
+ * @protected
+ * @param item {Y.AccordionItem} The item, which handles to remove
+ */
+ _removeItemHandles: function( item ){
+ var itemHandles, itemHandle;
+
+ itemHandles = this._itemsHandles[ item ];
+
+ for( itemHandle in itemHandles ){
+ if( itemHandles.hasOwnProperty( itemHandle ) ){
+ itemHandle = itemHandles[ itemHandle ];
+ itemHandle.detach();
+ }
+ }
+
+ delete this._itemsHandles[ item ];
+ },
+
+ /**
+ * Obtains the precise height of the node provided, including padding and border.
+ *
+ * @method _getNodeOffsetHeight
+ * @protected
+ * @param node {Node|HTMLElement} The node to gather the height from
+ * @return {Number} The calculated height or zero in case of failure
+ */
+ _getNodeOffsetHeight: function( node ){
+ var height, preciseRegion;
+
+ if( node instanceof Node ){
+ if( node.hasMethod( "getBoundingClientRect" ) ){
+ preciseRegion = node.invoke( "getBoundingClientRect" );
+
+ if( preciseRegion ){
+ height = preciseRegion.bottom - preciseRegion.top;
+
+ return height;
+ }
+ } else {
+ height = node.get( "offsetHeight" );
+ return Y.Lang.isValue( height ) ? height : 0;
+ }
+ } else if( node ){
+ height = node.offsetHeight;
+ return Y.Lang.isValue( height ) ? height : 0;
+ }
+
+ return 0;
+ },
+
+
+ /**
+ * Updates expand and alwaysVisible properties of given item with the values provided.
+ * The properties will be updated only if needed.
+ *
+ * @method _setItemProperties
+ * @protected
+ * @param item {Y.AccordionItem} The item, which properties should be updated
+ * @param expanding {Boolean} The new value of "expanded" property
+ * @param alwaysVisible {Boolean} The new value of "alwaysVisible" property
+ */
+ _setItemProperties: function( item, expanding, alwaysVisible ){
+ var curAlwaysVisible, curExpanded;
+
+ curAlwaysVisible = item.get( ALWAYSVISIBLE );
+ curExpanded = item.get( EXPANDED );
+
+ if( expanding != curExpanded ){
+ item.set( EXPANDED, expanding, {
+ internalCall: true
+ });
+ }
+
+ if( alwaysVisible !== curAlwaysVisible ){
+ item.set( ALWAYSVISIBLE, alwaysVisible, {
+ internalCall: true
+ });
+ }
+ },
+
+
+ /**
+ * Updates user interface of an item and marks it as expanded, alwaysVisible or both
+ *
+ * @method _setItemUI
+ * @protected
+ * @param item {Y.AccordionItem} The item, which user interface should be updated
+ * @param expanding {Boolean} If true, the item will be marked as expanded.
+ * If false, the item will be marked as collapsed
+ * @param alwaysVisible {Boolean} If true, the item will be marked as always visible.
+ * If false, the always visible mark will be removed
+ */
+ _setItemUI: function( item, expanding, alwaysVisible ){
+ item.markAsExpanded( expanding );
+ item.markAsAlwaysVisible( alwaysVisible );
+ },
+
+
+ /**
+ * Sets listener to resize event
+ *
+ * @method _afterRender
+ * @protected
+ * @param e {Event} after render custom event
+ */
+ _afterRender: function( e ){
+ var resizeEvent;
+
+ resizeEvent = this.get( "resizeEvent" );
+
+ this._setUpResizing( resizeEvent );
+
+ this.after( "resizeEventChange", Y.bind( this._afterResizeEventChange, this ) );
+ },
+
+
+ /**
+ * Set up resizing with the new value provided
+ *
+ * @method _afterResizeEventChange
+ * @protected
+ * @param params {Event} after resizeEventChange custom event
+ */
+ _afterResizeEventChange: function( params ){
+ this._setUpResizing( params.newVal );
+ },
+
+
+ /**
+ * Distributes the involved items as result of user interaction on item header.
+ * Some items might be stored in the list for collapsing, other in the list for expanding.
+ * Finally, invokes <code>_processItems</code> function, except if item has been expanded and
+ * user has clicked on always visible icon.
+ * If the user clicked on close icon, the item will be closed.
+ *
+ * @method _onItemChosen
+ * @protected
+ * @param item {Y.AccordionItem} The item on which user has clicked or pressed key
+ * @param srcIconAlwaysVisible {Boolean} True if the user has clicked on always visible icon
+ * @param srcIconClose {Boolean} True if the user has clicked on close icon
+ */
+ _onItemChosen: function( item, srcIconAlwaysVisible, srcIconClose ){
+ var toBeExcluded, alwaysVisible, expanded, collapseOthersOnExpand;
+
+ toBeExcluded = {};
+ collapseOthersOnExpand = this.get( COLLAPSEOTHERSONEXPAND );
+ alwaysVisible = item.get( ALWAYSVISIBLE );
+ expanded = item.get( EXPANDED );
+
+ if( srcIconClose ){
+ this.removeItem( item );
+ return;
+ } else if( srcIconAlwaysVisible ){
+ if( expanded ){
+ alwaysVisible = !alwaysVisible;
+ expanded = alwaysVisible ? true : expanded;
+
+ this._setItemProperties( item, expanded, alwaysVisible );
+ this._setItemUI( item, expanded, alwaysVisible );
+
+ return;
+ } else {
+ this._forExpanding[ item ] = {
+ 'item': item,
+ alwaysVisible: true
+ };
+
+ if( collapseOthersOnExpand ){
+ toBeExcluded[ item ] = {
+ 'item': item
+ };
+
+ this._storeItemsForCollapsing( toBeExcluded );
+ }
+ }
+ } else {
+ /*
+ * Do the opposite
+ */
+ if( expanded ){
+ this._forCollapsing[ item ] = {
+ 'item': item
+ };
+ } else {
+ this._forExpanding[ item ] = {
+ 'item': item,
+ 'alwaysVisible': alwaysVisible
+ };
+
+ if( collapseOthersOnExpand ){
+ toBeExcluded[ item ] = {
+ 'item': item
+ };
+
+ this._storeItemsForCollapsing( toBeExcluded );
+ }
+ }
+ }
+
+ this._processItems();
+ },
+
+
+ /**
+ * Helper method to adjust the height of all items, which <code>contentHeight</code> property is set as "stretch".
+ * If some item has animation running, it will be stopped before running another one.
+ *
+ * @method adjustStretchItems
+ * @protected
+ * @return {Number} The calculated height per strech item
+ */
+ _adjustStretchItems: function(){
+ var items = this.get( ITEMS ), heightPerStretchItem, forExpanding;
+
+ heightPerStretchItem = this._getHeightPerStretchItem();
+ forExpanding = this._forExpanding;
+
+ Y.Array.each( items, function( item, index, items ){
+ var body, bodyHeight, anim, heightSettings, expanded;
+
+ heightSettings = item.get( CONTENT_HEIGHT );
+ expanded = item.get( EXPANDED );
+
+ if( !forExpanding[ item ] && heightSettings.method === STRETCH && expanded ){
+ anim = this._animations[ item ];
+
+ // stop waiting animation
+ if( anim ){
+ anim.stop();
+ }
+
+ body = item.getStdModNode( WidgetStdMod.BODY );
+ bodyHeight = this._getNodeOffsetHeight( body );
+
+ if( heightPerStretchItem < bodyHeight ){
+ this._processCollapsing( item, heightPerStretchItem );
+ } else if( heightPerStretchItem > bodyHeight ){
+ this._processExpanding( item, heightPerStretchItem );
+ }
+ }
+ }, this );
+
+ return heightPerStretchItem;
+ },
+
+ /**
+ * Calculates the height per strech item.
+ *
+ * @method _getHeightPerStretchItem
+ * @protected
+ * @return {Number} The calculated height per strech item
+ */
+ _getHeightPerStretchItem: function(){
+ var height, items, stretchCounter = 0;
+
+ items = this.get( ITEMS );
+ height = this.get( BOUNDING_BOX ).get( "clientHeight" );
+
+ Y.Array.each( items, function( item, index, items ){
+ var collapsed, itemContentHeight, header, heightSettings, headerHeight;
+
+ header = item.getStdModNode( WidgetStdMod.HEADER );
+ heightSettings = item.get( CONTENT_HEIGHT );
+
+ headerHeight = this._getNodeOffsetHeight( header );
+
+ height -= headerHeight;
+ collapsed = !item.get( EXPANDED );
+
+ if( collapsed ){
+ height -= COLLAPSE_HEIGHT;
+ return;
+ }
+
+ if( heightSettings.method === STRETCH ){
+ stretchCounter++;
+ } else {
+ itemContentHeight = this._getItemContentHeight( item );
+ height -= itemContentHeight;
+ }
+ }, this );
+
+ if( stretchCounter > 0 ){
+ height /= stretchCounter;
+ }
+
+ if( height < 0 ){
+ height = 0;
+ }
+
+ return height;
+ },
+
+
+ /**
+ * Calculates the height of given item depending on its "contentHeight" property.
+ *
+ * @method _getItemContentHeight
+ * @protected
+ * @param item {Y.AccordionItem} The item, which height should be calculated
+ * @return {Number} The calculated item's height
+ */
+ _getItemContentHeight: function( item ){
+ var heightSettings, height = 0, body, bodyContent;
+
+ heightSettings = item.get( CONTENT_HEIGHT );
+
+ if( heightSettings.method === "auto" ){
+ body = item.getStdModNode( WidgetStdMod.BODY );
+ bodyContent = body.get( CHILDREN ).item(0);
+ height = bodyContent ? this._getNodeOffsetHeight( bodyContent ) : 0;
+ } else if( heightSettings.method === "fixed" ) {
+ height = heightSettings.height;
+ } else {
+ height = this._getHeightPerStretchItem();
+ }
+
+ return height;
+ },
+
+
+ /**
+ * Stores all items, which are expanded and not set as always visible in list
+ * in order to be collapsed later.
+ *
+ * @method _storeItemsForCollapsing
+ * @protected
+ * @param itemsToBeExcluded {Object} (optional) Contains one or more <code>Y.AccordionItem</code> instances,
+ * which should be not included in the list
+ */
+ _storeItemsForCollapsing: function( itemsToBeExcluded ){
+ var items;
+
+ itemsToBeExcluded = itemsToBeExcluded || {};
+ items = this.get( ITEMS );
+
+ Y.Array.each( items, function( item, index, items ){
+ var expanded, alwaysVisible;
+
+ expanded = item.get( EXPANDED );
+ alwaysVisible = item.get( ALWAYSVISIBLE );
+
+ if( expanded && !alwaysVisible && !itemsToBeExcluded[ item ] ){
+ this._forCollapsing[ item ] = {
+ 'item': item
+ };
+ }
+ }, this );
+ },
+
+
+ /**
+ * Expands an item to given height. This includes also an update to item's user interface
+ *
+ * @method _expandItem
+ * @protected
+ * @param item {Y.AccordionItem} The item, which should be expanded.
+ * @param height {Number} The height to which we should expand the item
+ */
+ _expandItem: function( item, height ){
+ var alwaysVisible = item.get( ALWAYSVISIBLE );
+
+ this._processExpanding( item, height );
+ this._setItemUI( item, true, alwaysVisible );
+ },
+
+
+ /**
+ * Expands an item to given height. Depending on the <code>useAnimation</code> setting,
+ * the process of expanding might be animated. This setting will be ignored, if <code>forceSkipAnimation</code> param
+ * is <code>true</code>.
+ *
+ * @method _processExpanding
+ * @protected
+ * @param item {Y.AccordionItem} An <code>Y.AccordionItem</code> instance to be expanded
+ * @param forceSkipAnimation {Boolean} If true, the animation will be skipped,
+ * without taking in consideration Accordion's <code>useAnimation</code> setting
+ * @param height {Number} The height to which item should be expanded
+ */
+ _processExpanding: function( item, height, forceSkipAnimation ){
+ var anim, curAnim, animSettings, notifyOthers = false,
+ accAnimationSettings, body;
+
+ body = item.getStdModNode( WidgetStdMod.BODY );
+
+ this.fire( BEFOREITEMERESIZED, {
+ 'item': item
+ });
+
+ if( body.get( "clientHeight" ) <= COLLAPSE_HEIGHT ){
+ notifyOthers = true;
+ this.fire( BEFOREITEMEXPAND, {
+ 'item': item
+ });
+ }
+
+ if( !forceSkipAnimation && this.get( "useAnimation" ) ){
+ animSettings = item.get( ANIMATION ) || {};
+
+ anim = new Anim( {
+ node: body,
+ to: {
+ 'height': height
+ }
+ });
+
+ anim.on( "end", Y.bind( this._onExpandComplete, this, item, notifyOthers ) );
+
+ accAnimationSettings = this.get( ANIMATION );
+
+ anim.set( "duration", animSettings.duration || accAnimationSettings.duration );
+ anim.set( "easing" , animSettings.easing || accAnimationSettings.easing );
+
+ curAnim = this._animations[ item ];
+
+ if( curAnim ){
+ curAnim.stop();
+ }
+
+ item.markAsExpanding( true );
+
+ this._animations[ item ] = anim;
+
+ anim.run();
+ } else {
+ body.setStyle( "height", height + PX );
+
+ this.fire( ITEMERESIZED, {
+ 'item': item
+ });
+
+ if( notifyOthers ){
+ this.fire( ITEMEXPANDED, {
+ 'item': item
+ });
+ }
+ }
+ },
+
+
+ /**
+ * Executes when animated expanding completes
+ *
+ * @method _onExpandComplete
+ * @protected
+ * @param item {Y.AccordionItem} An <code>Y.AccordionItem</code> instance which has been expanded
+ * @param notifyOthers {Boolean} If true, itemExpanded event will be fired
+ */
+ _onExpandComplete: function( item, notifyOthers ){
+ delete this._animations[ item ];
+
+ item.markAsExpanding( false );
+
+ this.fire( ITEMERESIZED, {
+ 'item': item
+ });
+
+ if( notifyOthers ){
+ this.fire( ITEMEXPANDED, {
+ 'item': item
+ });
+ }
+ },
+
+
+ /**
+ * Collapse an item and update its user interface
+ *
+ * @method _collapseItem
+ * @protected
+ * @param item {Y.AccordionItem} The item, which should be collapsed
+ */
+ _collapseItem: function( item ){
+ this._processCollapsing( item, COLLAPSE_HEIGHT );
+ this._setItemUI( item, false, false );
+ },
+
+
+ /**
+ * Collapse an item to given height. Depending on the <code>useAnimation</code> setting,
+ * the process of collapsing might be animated. This setting will be ignored, if <code>forceSkipAnimation</code> param
+ * is <code>true</code>.
+ *
+ * @method _processCollapsing
+ * @protected
+ * @param item {Y.AccordionItem} An <code>Y.AccordionItem</code> instance to be collapsed
+ * @param height {Number} The height to which item should be collapsed
+ * @param forceSkipAnimation {Boolean} If true, the animation will be skipped,
+ * without taking in consideration Accordion's <code>useAnimation</code> setting
+ */
+ _processCollapsing: function( item, height, forceSkipAnimation ){
+ var anim, curAnim, animSettings, accAnimationSettings, body,
+ notifyOthers = (height === COLLAPSE_HEIGHT);
+
+ body = item.getStdModNode( WidgetStdMod.BODY );
+
+
+ this.fire( BEFOREITEMERESIZED, {
+ 'item': item
+ });
+
+ if( notifyOthers ){
+ this.fire( BEFOREITEMCOLLAPSE, {
+ 'item': item
+ });
+ }
+
+ if( !forceSkipAnimation && this.get( "useAnimation" ) ){
+ animSettings = item.get( ANIMATION ) || {};
+
+ anim = new Anim( {
+ node: body,
+ to: {
+ 'height': height
+ }
+ });
+
+ anim.on( "end", Y.bind( this._onCollapseComplete, this, item, notifyOthers ) );
+
+ accAnimationSettings = this.get( ANIMATION );
+
+ anim.set( "duration", animSettings.duration || accAnimationSettings.duration );
+ anim.set( "easing" , animSettings.easing || accAnimationSettings.easing );
+
+ curAnim = this._animations[ item ];
+
+ if( curAnim ){
+ curAnim.stop();
+ }
+
+ item.markAsCollapsing( true );
+
+ this._animations[ item ] = anim;
+
+ anim.run();
+ } else {
+ body.setStyle( "height", height + PX );
+
+ this.fire( ITEMERESIZED, {
+ 'item': item
+ });
+
+ if( notifyOthers ){
+ this.fire( ITEMCOLLAPSED, {
+ 'item': item
+ });
+ }
+ }
+ },
+
+
+ /**
+ * Executes when animated collapsing completes
+ *
+ * @method _onCollapseComplete
+ * @protected
+ * @param item {Y.AccordionItem} An <code>Y.AccordionItem</code> instance which has been collapsed
+ * @param notifyOthers {Boolean} If true, itemCollapsed event will be fired
+ */
+ _onCollapseComplete: function( item, notifyOthers ){
+ delete this._animations[ item ];
+
+ item.markAsCollapsing( false );
+
+ this.fire( ITEMERESIZED, {
+ item: item
+ });
+
+ if( notifyOthers ){
+ this.fire( ITEMCOLLAPSED, {
+ 'item': item
+ });
+ }
+ },
+
+
+ /**
+ * Make an item draggable. The item can be reordered later.
+ *
+ * @method _initItemDragDrop
+ * @protected
+ * @param item {Y.AccordionItem} An <code>Y.AccordionItem</code> instance to be set as draggable
+ */
+ _initItemDragDrop: function( item ){
+ var itemHeader, dd, bb, itemBB, ddrop;
+
+ itemHeader = item.getStdModNode( WidgetStdMod.HEADER );
+
+ if( itemHeader.dd ){
+ return;
+ }
+
+ bb = this.get( BOUNDING_BOX );
+ itemBB = item.get( BOUNDING_BOX );
+
+ dd = new Y.DD.Drag({
+ node: itemHeader,
+ groups: [ DRAGGROUP ]
+ }).plug(Y.Plugin.DDProxy, {
+ moveOnEnd: false
+ }).plug(Y.Plugin.DDConstrained, {
+ constrain2node: bb
+ });
+
+ ddrop = new Y.DD.Drop({
+ node: itemBB,
+ groups: [ DRAGGROUP ]
+ });
+
+ dd.on ( "drag:start", Y.bind( this._onDragStart, this, dd ) );
+ dd.on ( "drag:end" , Y.bind( this._onDragEnd, this, dd ) );
+ dd.after( "drag:end" , Y.bind( this._afterDragEnd, this, dd ) );
+ dd.on ( "drag:drophit", Y.bind( this._onDropHit, this, dd ) );
+ },
+
+
+ /**
+ * Sets the label of the item being dragged on the drag proxy.
+ * Fires beforeItemReorder event - returning false will cancel reordering
+ *
+ * @method _onDragStart
+ * @protected
+ * @param dd {Y.DD.Drag} The drag instance of the item
+ * @param e {Event} the DD instance's drag:start custom event
+ */
+ _onDragStart: function( dd, e ){
+ var dragNode, item;
+
+ item = this.getItem( dd.get( NODE ).get( PARENT_NODE ) );
+ dragNode = dd.get( "dragNode" );
+
+ dragNode.addClass( C_PROXY_VISIBLE );
+ dragNode.set( "innerHTML", item.get( "label" ) );
+
+ return this.fire( BEFOREITEMREORDER, { 'item': item } );
+ },
+
+
+ /**
+ * Restores HTML structure of the drag proxy.
+ * Fires beforeEndItemReorder event - returning false will cancel reordering
+ *
+ * @method _onDragEnd
+ * @protected
+ * @param dd {Y.DD.Drag} The drag instance of the item
+ * @param e {Event} the DD instance's drag:end custom event
+ */
+ _onDragEnd: function( dd, e ){
+ var dragNode, item;
+
+ dragNode = dd.get( "dragNode" );
+
+ dragNode.removeClass( C_PROXY_VISIBLE );
+ dragNode.set( "innerHTML", "" );
+
+ item = this.getItem( dd.get( NODE ).get( PARENT_NODE ) );
+ return this.fire( BEFOREENDITEMREORDER, { 'item': item } );
+ },
+
+
+ /**
+ * Set drophit to false in dragdrop instance's custom value (if there has been drophit) and fires itemReordered event
+ *
+ * @method _afterDragEnd
+ * @protected
+ * @param dd {Y.DD.Drag} The drag instance of the item
+ * @param e {Event} the DD instance's drag:end custom event
+ */
+ _afterDragEnd: function( dd, e ){
+ var item, data;
+
+ data = dd.get( DATA );
+
+ if( data.drophit ){
+ item = this.getItem( dd.get( NODE ).get( PARENT_NODE ) );
+
+ dd.set( DATA, {
+ drophit: false
+ } );
+
+ return this.fire( ITEMREORDERED, { 'item': item } );
+ }
+
+ return true;
+ },
+
+
+ /**
+ * Moves the source item before or after target item.
+ *
+ * @method _onDropHit
+ * @protected
+ * @param dd {Y.DD.Drag} The drag instance of the item
+ * @param e {Event} the DD instance's drag:drophit custom event
+ */
+ _onDropHit: function( dd, e) {
+ var mineIndex, targetItemIndex, targetItemBB, itemBB, cb,
+ goingUp, items, targetItem, item;
+
+ item = this.getItem( dd.get( NODE ).get( PARENT_NODE ) );
+ targetItem = this.getItem( e.drop.get( NODE ) );
+
+ if( targetItem === item ){
+ return false;
+ }
+
+ mineIndex = this.getItemIndex( item );
+ targetItemIndex = this.getItemIndex( targetItem );
+ targetItemBB = targetItem.get( BOUNDING_BOX );
+ itemBB = item.get( BOUNDING_BOX );
+ cb = this.get( CONTENT_BOX );
+ goingUp = false;
+ items = this.get( ITEMS );
+
+ if( targetItemIndex < mineIndex ){
+ goingUp = true;
+ }
+
+ cb.removeChild( itemBB );
+
+ if( goingUp ){
+ cb. insertBefore( itemBB, targetItemBB );
+ items.splice( mineIndex, 1 );
+ items.splice( targetItemIndex, 0, item );
+ } else {
+ cb. insertBefore( itemBB, targetItemBB.next( C_ITEM ) );
+ items.splice( targetItemIndex + 1, 0, item );
+ items.splice( mineIndex, 1 );
+ }
+
+ dd.set( DATA, {
+ drophit: true
+ });
+
+ return true;
+ },
+
+
+ /**
+ * Process items as result of user interaction or properties change.
+ * This includes four steps:
+ * 1. Update the properties of the items
+ * 2. Collapse all items stored in the list for collapsing
+ * 3. Adjust all stretch items
+ * 4. Expand items stored in the list for expanding
+ *
+ * @method _processItems
+ * @protected
+ */
+ _processItems: function(){
+ var forCollapsing, forExpanding, itemCont, heightPerStretchItem,
+ height, heightSettings, item;
+
+ forCollapsing = this._forCollapsing;
+ forExpanding = this._forExpanding;
+
+ this._setItemsProperties();
+
+ for( item in forCollapsing ){
+ if( forCollapsing.hasOwnProperty( item ) ){
+ itemCont = forCollapsing[ item ];
+
+ this._collapseItem( itemCont.item );
+ }
+ }
+
+ heightPerStretchItem = this._adjustStretchItems();
+
+ for( item in forExpanding ){
+ if( forExpanding.hasOwnProperty( item ) ){
+ itemCont = forExpanding[ item ];
+ item = itemCont.item;
+ height = heightPerStretchItem;
+ heightSettings = item.get( CONTENT_HEIGHT );
+
+ if( heightSettings.method !== STRETCH ){
+ height = this._getItemContentHeight( item );
+ }
+
+ this._expandItem( item, height );
+ }
+ }
+
+ this._forCollapsing = {};
+ this._forExpanding = {};
+ },
+
+
+ /**
+ * Update properties of items, which were stored in the lists for collapsing or expanding
+ *
+ * @method _setItemsProperties
+ * @protected
+ */
+ _setItemsProperties: function (){
+ var forCollapsing, forExpanding, itemData;
+
+ forCollapsing = this._forCollapsing;
+ forExpanding = this._forExpanding;
+
+ for( itemData in forCollapsing ){
+ if( forCollapsing.hasOwnProperty( itemData ) ){
+ itemData = forCollapsing[ itemData ];
+ this._setItemProperties( itemData.item, false, false );
+ }
+ }
+
+ for( itemData in forExpanding ){
+ if( forExpanding.hasOwnProperty( itemData ) ){
+ itemData = forExpanding[ itemData ];
+ this._setItemProperties( itemData.item, true, itemData.alwaysVisible );
+ }
+ }
+ },
+
+
+ /**
+ * Handles the change of "expand" property of given item
+ *
+ * @method _afterItemExpand
+ * @protected
+ * @param params {EventFacade} The event facade for the attribute change
+ */
+ _afterItemExpand: function( params ){
+ var expanded, item, alwaysVisible, collapseOthersOnExpand;
+
+ if( params.internalCall ){
+ return;
+ }
+
+ expanded = params.newVal;
+ item = params.currentTarget;
+ alwaysVisible = item.get( ALWAYSVISIBLE );
+ collapseOthersOnExpand = this.get( COLLAPSEOTHERSONEXPAND );
+
+ if( expanded ){
+ this._forExpanding[ item ] = {
+ 'item': item,
+ 'alwaysVisible': alwaysVisible
+ };
+
+ if( collapseOthersOnExpand ){
+ this._storeItemsForCollapsing();
+ }
+ } else {
+ this._forCollapsing[ item ] = {
+ 'item': item
+ };
+ }
+
+ this._processItems();
+ },
+
+ /**
+ * Handles the change of "alwaysVisible" property of given item
+ *
+ * @method _afterItemAlwaysVisible
+ * @protected
+ * @param params {EventFacade} The event facade for the attribute change
+ */
+ _afterItemAlwaysVisible: function( params ){
+ var item, alwaysVisible, expanded;
+
+ if( params.internalCall ){
+ return;
+ }
+
+ alwaysVisible = params.newVal;
+ item = params.currentTarget;
+ expanded = item.get( EXPANDED );
+
+ if( alwaysVisible ){
+ if( expanded ){
+ this._setItemProperties( item, true, true );
+ this._setItemUI( item, true, true );
+ return;
+ } else {
+ this._forExpanding[ item ] = {
+ 'item': item,
+ 'alwaysVisible': true
+ };
+
+ this._storeItemsForCollapsing();
+ }
+ } else {
+ if( expanded ){
+ this._setItemUI( item, true, false );
+ return;
+ } else {
+ return;
+ }
+ }
+
+ this._processItems();
+ },
+
+
+ /**
+ * Handles the change of "contentHeight" property of given item
+ *
+ * @method _afterContentHeight
+ * @protected
+ * @param params {EventFacade} The event facade for the attribute change
+ */
+ _afterContentHeight: function( params ){
+ var item, itemContentHeight, body, bodyHeight, expanded;
+
+ item = params.currentTarget;
+
+ this._adjustStretchItems();
+
+ if( params.newVal.method !== STRETCH ){
+ expanded = item.get( EXPANDED );
+ itemContentHeight = this._getItemContentHeight( item );
+
+ body = item.getStdModNode( WidgetStdMod.BODY );
+ bodyHeight = this._getNodeOffsetHeight( body );
+
+ if( itemContentHeight < bodyHeight ){
+ this._processCollapsing( item, itemContentHeight, !expanded );
+ } else if( itemContentHeight > bodyHeight ){
+ this._processExpanding( item, itemContentHeight, !expanded );
+ }
+ }
+ },
+
+
+ /**
+ * Handles the change of "contentUpdate" property of given item
+ *
+ * @method _afterContentUpdate
+ * @protected
+ * @param params {EventFacade} The event facade for the attribute change
+ */
+ _afterContentUpdate : function( params ){
+ var item, body, bodyHeight, expanded, auto, anim;
+
+ item = params.currentTarget;
+ auto = item.get( "contentHeight" ).method === "auto";
+ expanded = item.get( EXPANDED );
+
+ body = item.getStdModNode( WidgetStdMod.BODY );
+ bodyHeight = this._getNodeOffsetHeight( body );
+
+ if( auto && expanded && params.src !== Y.Widget.UI_SRC ){
+ Y.later( 0, this, function(){
+ var itemContentHeight = this._getItemContentHeight( item );
+
+ if( itemContentHeight !== bodyHeight ){
+ anim = this._animations[ item ];
+
+ // stop waiting animation
+ if( anim ){
+ anim.stop();
+ }
+
+ this._adjustStretchItems();
+
+ if( itemContentHeight < bodyHeight ){
+ this._processCollapsing( item, itemContentHeight, !expanded );
+ } else if( itemContentHeight > bodyHeight ){
+ this._processExpanding( item, itemContentHeight, !expanded );
+ }
+ }
+ } );
+ }
+ },
+
+
+ /**
+ * Subscribe for resize event, which could be provided from the browser or from an arbitrary object.
+ * For example, if there is LayoutManager in the page, it is preferable to subscribe to its resize event,
+ * instead to those, which browser provides.
+ *
+ * @method _setUpResizing
+ * @protected
+ * @param value {String|Object} String "default" or object with the following properties:
+ * <dl>
+ * <dt>sourceObject</dt>
+ * <dd>An abbitrary object</dd>
+ * <dt>resizeEvent</dt>
+ * <dd>The name of its resize event</dd>
+ * </dl>
+ */
+ _setUpResizing: function( value ){
+ if( this._resizeEventHandle ){
+ this._resizeEventHandle.detach();
+ }
+
+ if( value === DEFAULT ){
+ this._resizeEventHandle = Y.on( 'windowresize', Y.bind( this._adjustStretchItems, this ) );
+ } else {
+ this._resizeEventHandle = value.sourceObject.on( value.resizeEvent, Y.bind( this._adjustStretchItems, this ) );
+ }
+ },
+
+
+ /**
+ * Creates one or more items found in Accordion's <code>contentBox</code>
+ *
+ * @method renderUI
+ * @protected
+ */
+ renderUI: function(){
+ var srcNode, itemsDom, contentBox, srcNodeId;
+
+ srcNode = this.get( SRCNODE );
+ contentBox = this.get( CONTENT_BOX );
+ srcNodeId = srcNode.get( "id" );
+
+ /*
+ * Widget 3.1 workaround - the Id of contentBox is generated by YUI, instead to keep srcNode's Id, so we set it manually
+ */
+ contentBox.set( "id", srcNodeId );
+
+ itemsDom = srcNode.all( "> ." + C_ITEM );
+
+ itemsDom.each( function( itemNode, index, itemsDom ){
+ var newItem;
+
+ if( !this.getItem( itemNode ) ){
+ newItem = new Y.AccordionItem({
+ srcNode: itemNode,
+ id : itemNode.get( "id" )
+ });
+
+ this.addItem( newItem );
+ }
+ }, this );
+ },
+
+
+ /**
+ * Add listener to <code>itemChosen</code> event in Accordion's content box
+ *
+ * @method bindUI
+ * @protected
+ */
+ bindUI: function(){
+ var contentBox, itemChosenEvent;
+
+ contentBox = this.get( CONTENT_BOX );
+ itemChosenEvent = this.get( 'itemChosen' );
+
+ contentBox.delegate( itemChosenEvent, Y.bind( this._onItemChosenEvent, this ), '.yui3-widget-hd' );
+ },
+
+
+ /**
+ * Listening for itemChosen event, determines the source (is that iconClose, iconAlwaysVisisble, etc.) and
+ * invokes this._onItemChosen for further processing
+ *
+ * @method _onItemChosenEvent
+ * @protected
+ *
+ * @param e {Event} The itemChosen event
+ */
+ _onItemChosenEvent: function(e){
+ var header, itemNode, item, iconAlwaysVisible,
+ iconClose, srcIconAlwaysVisible, srcIconClose;
+
+ header = e.currentTarget;
+ itemNode = header.get( PARENT_NODE );
+ item = this.getItem( itemNode );
+ iconAlwaysVisible = item.get( ICON_ALWAYSVISIBLE );
+ iconClose = item.get( ICON_CLOSE );
+ srcIconAlwaysVisible = (iconAlwaysVisible === e.target);
+ srcIconClose = (iconClose === e.target);
+
+ this._onItemChosen( item, srcIconAlwaysVisible, srcIconClose );
+ },
+
+
+ /**
+ * Add an item to Accordion. Items could be added/removed multiple times and they
+ * will be rendered in the process of adding, if not.
+ * The item will be expanded, collapsed, or set as always visible depending on the
+ * settings. Item's properties will be also updated, if they are incomplete.
+ * For example, if <code>alwaysVisible</code> is true, but <code>expanded</code>
+ * property is false, it will be set to true also.
+ *
+ * If the second param, <code>parentItem</code> is an <code>Y.AccordionItem</code> instance,
+ * registered in Accordion, the item will be added as child of the <code>parentItem</code>
+ *
+ * @method addItem
+ * @param item {Y.AccordionItem} The item to be added in Accordion
+ * @param parentItem {Y.AccordionItem} (optional) This item will be the parent of the item being added
+ *
+ * @return {Boolean} True in case of successfully added item, false otherwise
+ */
+ addItem: function( item, parentItem ){
+ var expanded, alwaysVisible, itemBody, itemBodyContent, itemIndex, items, contentBox,
+ itemHandles, itemContentBox, res, children;
+
+ res = this.fire( BEFOREITEMADD, {
+ 'item': item
+ });
+
+ if( !res ){
+ return false;
+ }
+
+ items = this.get( ITEMS );
+ contentBox = this.get( CONTENT_BOX );
+
+ itemContentBox = item.get( CONTENT_BOX );
+
+ if( !itemContentBox.inDoc() ){
+ if( parentItem ){
+ itemIndex = this.getItemIndex( parentItem );
+
+ if( itemIndex < 0 ){
+ return false;
+ }
+
+ items.splice( itemIndex, 0, item );
+ contentBox.insertBefore( itemContentBox, parentItem.get( BOUNDING_BOX ) );
+ } else {
+ items.push( item );
+ contentBox.insertBefore( itemContentBox, null );
+ }
+ } else {
+ children = contentBox.get( CHILDREN );
+
+ res = children.some( function( node, index, nodeList ){
+ if( node === itemContentBox ){
+ items.splice( index, 0, item );
+ return true;
+ } else {
+ return false;
+ }
+ }, this );
+
+ if( !res ){
+ return false;
+ }
+ }
+
+ itemBody = item.getStdModNode( WidgetStdMod.BODY );
+ itemBodyContent = item.get( BODYCONTENT );
+
+ if( !itemBody && !itemBodyContent ){
+ item.set( BODYCONTENT, "" );
+ }
+
+ if( !item.get( RENDERED ) ){
+ item.render();
+ }
+
+ expanded = item.get( EXPANDED );
+ alwaysVisible = item.get( ALWAYSVISIBLE );
+
+ expanded = expanded || alwaysVisible;
+
+ if( expanded ){
+ this._forExpanding[ item ] = {
+ 'item': item,
+ 'alwaysVisible': alwaysVisible
+ };
+ } else {
+ this._forCollapsing[ item ] = {
+ 'item': item
+ };
+ }
+
+ this._processItems();
+
+ if( this.get( "reorderItems" ) ){
+ this._initItemDragDrop( item );
+ }
+
+ itemHandles = this._itemsHandles[ item ];
+
+ if( !itemHandles ){
+ itemHandles = {};
+ }
+
+ itemHandles = {
+ "expandedChange" : item.after( "expandedChange", Y.bind( this._afterItemExpand, this ) ),
+ "alwaysVisibleChange" : item.after( "alwaysVisibleChange", Y.bind( this._afterItemAlwaysVisible, this ) ),
+ "contentHeightChange" : item.after( "contentHeightChange", Y.bind( this._afterContentHeight, this ) ),
+ "contentUpdate" : item.after( "contentUpdate", Y.bind( this._afterContentUpdate, this ) )
+ };
+
+ this._itemsHandles[ item ] = itemHandles;
+
+ this.fire( ITEMADDED, {
+ 'item': item
+ });
+
+ return true;
+ },
+
+
+ /**
+ * Removes an previously registered item in Accordion
+ *
+ * @method removeItem
+ * @param p_item {Y.AccordionItem|Number} The item to be removed, or its index
+ * @return {Y.AccordionItem} The removed item or null if not found
+ */
+ removeItem: function( p_item ){
+ var items, bb, item = null, itemIndex;
+
+ items = this.get( ITEMS );
+
+ if( Lang.isNumber( p_item ) ){
+ itemIndex = p_item;
+ } else if( p_item instanceof Y.AccordionItem ){
+ itemIndex = this.getItemIndex( p_item );
+ } else {
+ return null;
+ }
+
+ if( itemIndex >= 0 ){
+
+ this.fire( BEFOREITEMREMOVE, {
+ item: p_item
+ });
+
+ item = items.splice( itemIndex, 1 )[0];
+
+ this._removeItemHandles( item );
+
+ bb = item.get( BOUNDING_BOX );
+ bb.remove();
+
+ this._adjustStretchItems();
+
+ this.fire( ITEMREMOVED, {
+ item: p_item
+ });
+ }
+
+ return item;
+ },
+
+
+ /**
+ * Searching for item, previously registered in Accordion
+ *
+ * @method getItem
+ * @param param {Number|Y.Node} If number, this must be item's index.
+ * If Node, it should be the value of item's <code>contentBox</code> or <code>boundingBox</code> properties
+ *
+ * @return {Y.AccordionItem} The found item or null
+ */
+ getItem: function( param ){
+ var items = this.get( ITEMS ), item = null;
+
+ if( Lang.isNumber( param ) ){
+ item = items[ param ];
+ return (item instanceof Y.AccordionItem) ? item : null;
+ } else if( param instanceof Node ){
+ Y.Array.some( items, function( tmpItem, index, items ){
+ var contentBox = tmpItem.get( CONTENT_BOX );
+
+ /*
+ * Both contentBox and boundingBox point to same node, so it is safe to check only one of them
+ */
+ if( contentBox === param ){
+ item = tmpItem;
+ return true;
+ } else {
+ return false;
+ }
+ }, this );
+ }
+
+ return item;
+ },
+
+
+ /**
+ * Looking for the index of previously registered item
+ *
+ * @method getItemIndex
+ * @param item {Y.AccordionItem} The item which index should be returned
+ * @return {Number} Item index or <code>-1</code> if item has been not found
+ */
+ getItemIndex: function( item ){
+ var res = -1, items;
+
+ if( item instanceof Y.AccordionItem ){
+ items = this.get( ITEMS );
+
+ Y.Array.some( items, function( tmpItem, index, items ){
+ if( tmpItem === item ){
+ res = index;
+ return true;
+ } else {
+ return false;
+ }
+ }, this );
+ }
+
+ return res;
+ },
+
+
+ /**
+ * Overwrites Y.WidgetStdMod fuction in order to resolve Widget 3.1 issue:<br>
+ * If CONTENT_TEMPLATE is null, in renderUI the result of the following code:
+ * <code>this.getStdModNode( Y.WidgetStdMod.HEADER );</code> is null.
+ * The same is with <code>this.getStdModNode( Y.WidgetStdMod.BODY );</code>.
+ *
+ * @method _findStdModSection
+ * @protected
+ * @param {String} section The section for which the render Node is to be found. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @return {Node} The rendered node for the given section, or null if not found.
+ */
+ _findStdModSection: function(section) {
+ return this.get(SRCNODE).one("> ." + Y.WidgetStdMod.SECTION_CLASS_NAMES[section]);
+ },
+
+ CONTENT_TEMPLATE : null
+}, {
+ /**
+ * Static property provides a string to identify the class.
+ *
+ * @property Accordion.NAME
+ * @type String
+ * @static
+ */
+ NAME : AccName,
+
+ /**
+ * Static property used to define the default attribute
+ * configuration for the Accordion.
+ *
+ * @property Accordion.ATTRS
+ * @type Object
+ * @static
+ */
+ ATTRS : {
+ /**
+ * @description The event on which Accordion should listen for user interactions.
+ * The value can be also mousedown or mouseup. Mousedown event can be used if
+ * drag&drop is not enabled
+ *
+ * @attribute itemChosen
+ * @default click
+ * @type String
+ */
+ itemChosen: {
+ value: "click",
+ validator: Lang.isString
+ },
+
+ /**
+ * @description Contains the items, currently added to Accordion
+ *
+ * @attribute items
+ * @readOnly
+ * @default []
+ * @type Array
+ */
+ items: {
+ value: [],
+ readOnly: true,
+ validator: Lang.isArray
+ },
+
+ /**
+ * @attribute resizeEvent
+ *
+ * @description The event on which Accordion should listen for resizing.
+ * The value must be one of these:
+ * <ul>
+ * <li> String "default" - the Accordion will subscribe to Y.windowresize event
+ * </li>
+ * <li> An object in the following form:
+ * {
+ * sourceObject: some_javascript_object,
+ * resizeEvent: an_event_to_subscribe
+ * }
+ * </li>
+ * </ul>
+ * For example, if we are using LayoutManager's instance as sourceObject, we will have to use its "resize" event as resizeEvent
+ *
+ * @default "default"
+ * @type String or Object
+ */
+
+ resizeEvent: {
+ value: DEFAULT,
+ validator: function( value ){
+ if( value === DEFAULT ){
+ return true;
+ } else if( Lang.isObject(value) ){
+ if( Lang.isValue( value.sourceObject ) && Lang.isValue( value.resizeEvent ) ){
+ return true;
+ }
+ }
+
+ return false;
+ }
+ },
+
+ /**
+ * @attribute useAnimation
+ * @description Boolean indicating that Accordion should use animation when expanding or collapsing items.
+ *
+ * @default true
+ * @type Boolean
+ */
+ useAnimation: {
+ value: true,
+ validator: Lang.isBoolean
+ },
+
+ /**
+ * @attribute animation
+ * @description Animation config values, see Y.Animation
+ *
+ * @default <code> {
+ * duration: 1,
+ * easing: Easing.easeOutStrong
+ * }
+ * </code>
+ *
+ * @type Object
+ */
+ animation: {
+ value: {
+ duration: 1,
+ easing: Easing.easeOutStrong
+ },
+ validator: function( value ){
+ return Lang.isObject( value ) && Lang.isNumber( value.duration ) &&
+ Lang.isFunction( value.easing );
+ }
+ },
+
+ /**
+ * @attribute reorderItems
+ * @description Boolean indicating that items can be reordered via drag and drop.<br>
+ *
+ * Enabling items reordering requires also including the optional drag and drop modules in YUI instance:<br>
+ * 'dd-constrain', 'dd-proxy', 'dd-drop', or just 'dd'
+ *
+ * @default false
+ * @type Boolean
+ */
+ reorderItems: {
+ value: false,
+ validator: function(value){
+ return Lang.isBoolean(value) && !Lang.isUndefined( Y.DD );
+ }
+ },
+
+ /**
+ * @attribute collapseOthersOnExpand
+ * @description If true, on item expanding, all other expanded and not set as always visible items, will be collapsed
+ * Otherwise, they will stay open
+ *
+ * @default true
+ * @type Boolean
+ */
+ collapseOthersOnExpand: {
+ value: true,
+ validator: Lang.isBoolean
+ }
+ }
+});
+
+}());
+
+/**
+ * Provides AccordionItem class
+ *
+ * @module gallery-accordion
+ */
+
+(function(){
+
+// Local constants
+var Lang = Y.Lang,
+ Node = Y.Node,
+ JSON = Y.JSON,
+ WidgetStdMod = Y.WidgetStdMod,
+ AccItemName = "accordion-item",
+ getCN = Y.ClassNameManager.getClassName,
+
+ C_ICONEXPANDED_EXPANDING = getCN( AccItemName, "iconexpanded", "expanding" ),
+ C_ICONEXPANDED_COLLAPSING = getCN( AccItemName, "iconexpanded", "collapsing" ),
+
+ C_ICON = getCN( AccItemName, "icon" ),
+ C_LABEL = getCN( AccItemName, "label" ),
+ C_ICONALWAYSVISIBLE = getCN( AccItemName, "iconalwaysvisible" ),
+ C_ICONSCONTAINER = getCN( AccItemName, "icons" ),
+ C_ICONEXPANDED = getCN( AccItemName, "iconexpanded" ),
+ C_ICONCLOSE = getCN( AccItemName, "iconclose" ),
+ C_ICONCLOSE_HIDDEN = getCN( AccItemName, "iconclose", "hidden" ),
+
+ C_ICONEXPANDED_ON = getCN( AccItemName, "iconexpanded", "on" ),
+ C_ICONEXPANDED_OFF = getCN( AccItemName, "iconexpanded", "off" ),
+
+ C_ICONALWAYSVISIBLE_ON = getCN( AccItemName, "iconalwaysvisible", "on" ),
+ C_ICONALWAYSVISIBLE_OFF = getCN( AccItemName, "iconalwaysvisible", "off" ),
+
+ C_EXPANDED = getCN( AccItemName, "expanded" ),
+ C_CLOSABLE = getCN( AccItemName, "closable" ),
+ C_ALWAYSVISIBLE = getCN( AccItemName, "alwaysvisible" ),
+ C_CONTENTHEIGHT = getCN( AccItemName, "contentheight" ),
+
+ TITLE = "title",
+ STRINGS = "strings",
+ RENDERED = "rendered",
+ CLASS_NAME = "className",
+ AUTO = "auto",
+ STRETCH = "stretch",
+ FIXED = "fixed",
+ HEADER_SELECTOR = ".yui3-widget-hd",
+ DOT = ".",
+ HEADER_SELECTOR_SUB = ".yui3-widget-hd " + DOT,
+ INNER_HTML = "innerHTML",
+ ICONS_CONTAINER = "iconsContainer",
+ ICON = "icon",
+ NODE_LABEL = "nodeLabel",
+ ICON_ALWAYSVISIBLE = "iconAlwaysVisible",
+ ICON_EXPANDED = "iconExpanded",
+ ICON_CLOSE = "iconClose",
+ HREF = "href",
+ HREF_VALUE = "#",
+ YUICONFIG = "yuiConfig",
+
+ REGEX_TRUE = /^(?:true|yes|1)$/,
+ REGEX_AUTO = /^auto\s*/,
+ REGEX_STRETCH = /^stretch\s*/,
+ REGEX_FIXED = /^fixed-\d+/;
+
+/**
+ * Create an AccordionItem widget.
+ *
+ * @class AccordionItem
+ * @extends Widget
+ */
+
+Y.AccordionItem = Y.Base.create( AccItemName, Y.Widget, [Y.WidgetStdMod], {
+ /**
+ * Creates the header content
+ *
+ * @method _createHeader
+ * @protected
+ */
+ _createHeader: function(){
+ var closable, templates, strings, iconsContainer,
+ icon, nodeLabel, iconExpanded, iconAlwaysVisible, iconClose;
+
+ icon = this.get( ICON );
+ nodeLabel = this.get( NODE_LABEL );
+ iconExpanded = this.get( ICON_EXPANDED );
+ iconAlwaysVisible = this.get( ICON_ALWAYSVISIBLE );
+ iconClose = this.get( ICON_CLOSE );
+ iconsContainer = this.get( ICONS_CONTAINER );
+
+ strings = this.get( STRINGS );
+ closable = this.get( "closable" );
+ templates = Y.AccordionItem.TEMPLATES;
+
+ if( !icon ){
+ icon = Node.create( templates.icon );
+ this.set( ICON, icon );
+ }
+
+ if( !nodeLabel ){
+ nodeLabel = Node.create( templates.label );
+ this.set( NODE_LABEL, nodeLabel );
+ } else if( !nodeLabel.hasAttribute( HREF ) ){
+ nodeLabel.setAttribute( HREF, HREF_VALUE );
+ }
+
+ nodeLabel.setContent( this.get( "label" ) );
+
+
+ if( !iconsContainer ){
+ iconsContainer = Node.create( templates.iconsContainer );
+ this.set( ICONS_CONTAINER, iconsContainer );
+ }
+
+ if( !iconAlwaysVisible ){
+ iconAlwaysVisible = Node.create( templates.iconAlwaysVisible );
+ iconAlwaysVisible.setAttribute( TITLE, strings.title_always_visible_off );
+ this.set( ICON_ALWAYSVISIBLE, iconAlwaysVisible );
+ } else if( !iconAlwaysVisible.hasAttribute( HREF ) ){
+ iconAlwaysVisible.setAttribute( HREF, HREF_VALUE );
+ }
+
+
+ if( !iconExpanded ){
+ iconExpanded = Node.create( templates.iconExpanded );
+ iconExpanded.setAttribute( TITLE, strings.title_iconexpanded_off );
+ this.set( ICON_EXPANDED, iconExpanded );
+ } else if( !iconExpanded.hasAttribute( HREF ) ){
+ iconExpanded.setAttribute( HREF, HREF_VALUE );
+ }
+
+
+ if( !iconClose ){
+ iconClose = Node.create( templates.iconClose );
+ iconClose.setAttribute( TITLE, strings.title_iconclose );
+ this.set( ICON_CLOSE, iconClose );
+ } else if( !iconClose.hasAttribute( HREF ) ){
+ iconClose.setAttribute( HREF, HREF_VALUE );
+ }
+
+ if( closable ){
+ iconClose.removeClass( C_ICONCLOSE_HIDDEN );
+ } else {
+ iconClose.addClass( C_ICONCLOSE_HIDDEN );
+ }
+
+ this._addHeaderComponents();
+ },
+
+ /**
+ * Add label and icons in the header. Also, it creates header in if not set from markup
+ *
+ * @method _addHeaderComponents
+ * @protected
+ */
+ _addHeaderComponents: function(){
+ var header, icon, nodeLabel, iconsContainer, iconExpanded,
+ iconAlwaysVisible, iconClose;
+
+ icon = this.get( ICON );
+ nodeLabel = this.get( NODE_LABEL );
+ iconExpanded = this.get( ICON_EXPANDED );
+ iconAlwaysVisible = this.get( ICON_ALWAYSVISIBLE );
+ iconClose = this.get( ICON_CLOSE );
+ iconsContainer = this.get( ICONS_CONTAINER );
+
+ header = this.getStdModNode( WidgetStdMod.HEADER );
+
+ if( !header ){
+ header = new Node( document.createDocumentFragment() );
+ header.appendChild( icon );
+ header.appendChild( nodeLabel );
+ header.appendChild( iconsContainer );
+ iconsContainer.appendChild( iconAlwaysVisible );
+ iconsContainer.appendChild( iconExpanded );
+ iconsContainer.appendChild( iconClose );
+
+ this.setStdModContent( WidgetStdMod.HEADER, header, WidgetStdMod.REPLACE );
+ } else {
+ if( !header.contains( icon ) ){
+ if( header.contains( nodeLabel ) ){
+ header.insertBefore( icon, nodeLabel );
+ } else {
+ header.appendChild( icon );
+ }
+ }
+
+ if( !header.contains( nodeLabel ) ){
+ header.appendChild( nodeLabel );
+ }
+
+ if( !header.contains( iconsContainer ) ){
+ header.appendChild( iconsContainer );
+ }
+
+ if( !iconsContainer.contains( iconAlwaysVisible ) ){
+ iconsContainer.appendChild( iconAlwaysVisible );
+ }
+
+ if( !iconsContainer.contains( iconExpanded ) ){
+ iconsContainer.appendChild( iconExpanded );
+ }
+
+ if( !iconsContainer.contains( iconClose ) ){
+ iconsContainer.appendChild( iconClose );
+ }
+ }
+ },
+
+
+ /**
+ * Handles the change of "labelChanged" property. Updates item's UI with the label provided
+ *
+ * @method _labelChanged
+ * @protected
+ * @param params {EventFacade} The event facade for the attribute change
+ */
+ _labelChanged: function( params ){
+ var label;
+
+ if( this.get( RENDERED ) ){
+ label = this.get( NODE_LABEL );
+ label.set( INNER_HTML, params.newVal );
+ }
+ },
+
+
+ /**
+ * Handles the change of "closableChanged" property. Hides or shows close icon
+ *
+ * @method _closableChanged
+ * @protected
+ * @param params {EventFacade} The event facade for the attribute change
+ */
+ _closableChanged: function( params ){
+ var iconClose;
+
+ if( this.get( RENDERED ) ){
+ iconClose = this.get( ICON_CLOSE );
+
+ if( params.newVal ){
+ iconClose.removeClass( C_ICONCLOSE_HIDDEN );
+ } else {
+ iconClose.addClass( C_ICONCLOSE_HIDDEN );
+ }
+ }
+ },
+
+
+ /**
+ * Initializer lifecycle implementation for the AccordionItem class.
+ *
+ * @method initializer
+ * @protected
+ * @param config {Object} Configuration object literal for the AccordionItem
+ */
+ initializer: function( config ) {
+ this.after( "labelChange", Y.bind( this._labelChanged, this ) );
+ this.after( "closableChange", Y.bind( this._closableChanged, this ) );
+ },
+
+
+ /**
+ * Destructor lifecycle implementation for the AccordionItem class.
+ *
+ * @method destructor
+ * @protected
+ */
+ destructor : function() {
+ // EMPTY
+ },
+
+
+ /**
+ * Creates AccordionItem's header.
+ *
+ * @method renderUI
+ * @protected
+ */
+ renderUI: function(){
+ this._createHeader();
+ },
+
+ /**
+ * Configures/Sets up listeners to bind Widget State to UI/DOM
+ *
+ * @method bindUI
+ * @protected
+ */
+ bindUI: function(){
+ var contentBox = this.get( "contentBox" );
+
+ contentBox.delegate( "click", Y.bind( this._onLinkClick, this ), HEADER_SELECTOR + ' a' );
+ },
+
+
+
+ /**
+ * Prevent default action on clicking the link in the label
+ *
+ * @method _onLinkClick
+ * @protected
+ *
+ * @param e {Event} The click event
+ */
+ _onLinkClick: function( e ){
+ e.preventDefault();
+ },
+
+ /**
+ * Marks the item as always visible by adding class to always visible icon.
+ * The icon will be updated only if needed.
+ *
+ * @method markAsAlwaysVisible
+ * @param alwaysVisible {Boolean} If true, the item should be marked as always visible.
+ * @return {Boolean} Return true if the icon has been updated, false if there was no need to update
+ */
+ markAsAlwaysVisible: function( alwaysVisible ){
+ var iconAlwaysVisisble, strings;
+
+ iconAlwaysVisisble = this.get( ICON_ALWAYSVISIBLE );
+ strings = this.get( STRINGS );
+
+ if( alwaysVisible ){
+ if( !iconAlwaysVisisble.hasClass( C_ICONALWAYSVISIBLE_ON ) ){
+ iconAlwaysVisisble.replaceClass( C_ICONALWAYSVISIBLE_OFF, C_ICONALWAYSVISIBLE_ON );
+ iconAlwaysVisisble.set( TITLE, strings.title_always_visible_on );
+ return true;
+ }
+ } else {
+ if( iconAlwaysVisisble.hasClass( C_ICONALWAYSVISIBLE_ON ) ){
+ iconAlwaysVisisble.replaceClass( C_ICONALWAYSVISIBLE_ON, C_ICONALWAYSVISIBLE_OFF );
+ iconAlwaysVisisble.set( TITLE, strings.title_always_visible_off );
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+
+ /**
+ * Marks the item as expanded by adding class to expand icon.
+ * The icon will be updated only if needed.
+ *
+ * @method markAsExpanded
+ * @param expanded {Boolean} Boolean indicating that item should be marked as expanded.
+ * @return {Boolean} Return true if the icon has been updated, false if there was no need to update
+ */
+ markAsExpanded: function( expanded ){
+ var strings, iconExpanded;
+
+ iconExpanded = this.get( ICON_EXPANDED );
+ strings = this.get( STRINGS );
+
+ if( expanded ){
+ if( !iconExpanded.hasClass( C_ICONEXPANDED_ON ) ){
+ iconExpanded.replaceClass( C_ICONEXPANDED_OFF, C_ICONEXPANDED_ON );
+ iconExpanded.set( TITLE , strings.title_iconexpanded_on );
+ return true;
+ }
+ } else {
+ if( iconExpanded.hasClass( C_ICONEXPANDED_ON ) ){
+ iconExpanded.replaceClass( C_ICONEXPANDED_ON, C_ICONEXPANDED_OFF );
+ iconExpanded.set( TITLE , strings.title_iconexpanded_off );
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+
+ /**
+ * Marks the item as expanding by adding class to expand icon.
+ * The method will update icon only if needed.
+ *
+ * @method markAsExpanding
+ * @param expanding {Boolean} Boolean indicating that the item should be marked as expanding.
+ * @return {Boolean} Return true if the icon has been updated, false if there was no need to update
+ */
+ markAsExpanding: function( expanding ){
+ var iconExpanded = this.get( ICON_EXPANDED );
+
+ if( expanding ){
+ if( !iconExpanded.hasClass( C_ICONEXPANDED_EXPANDING ) ){
+ iconExpanded.addClass( C_ICONEXPANDED_EXPANDING );
+ return true;
+ }
+ } else {
+ if( iconExpanded.hasClass( C_ICONEXPANDED_EXPANDING ) ){
+ iconExpanded.removeClass( C_ICONEXPANDED_EXPANDING );
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+
+ /**
+ * Marks the item as collapsing by adding class to expand icon.
+ * The method will update icon only if needed.
+ *
+ * @method markAsCollapsing
+ * @param collapsing {Boolean} Boolean indicating that the item should be marked as collapsing.
+ * @return {Boolean} Return true if the icon has been updated, false if there was no need to update
+ */
+ markAsCollapsing: function( collapsing ){
+ var iconExpanded = this.get( ICON_EXPANDED );
+
+ if( collapsing ){
+ if( !iconExpanded.hasClass( C_ICONEXPANDED_COLLAPSING ) ){
+ iconExpanded.addClass( C_ICONEXPANDED_COLLAPSING );
+ return true;
+ }
+ } else {
+ if( iconExpanded.hasClass( C_ICONEXPANDED_COLLAPSING ) ){
+ iconExpanded.removeClass( C_ICONEXPANDED_COLLAPSING );
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+
+ /**
+ * Forces the item to resize as result of direct content manipulation (via 'innerHTML').
+ * This method should be invoked if 'contentHeight' property has been set to 'auto'.
+ *
+ * @method resize
+ */
+ resize : function(){
+ this.fire( "contentUpdate" );
+ },
+
+
+ /**
+ * Parses and returns the value of contentHeight property, if set method "fixed".
+ * The value must be in this format: fixed-X, where X is integer
+ *
+ * @method _extractFixedMethodValue
+ * @param value {String} The value to be parsed
+ * @return {Number} The parsed value or null
+ * @protected
+ */
+ _extractFixedMethodValue: function( value ){
+ var i, length, chr, height = null;
+
+ for( i = 6, length = value.length; i < length; i++ ){ // 6 = "fixed-".length
+ chr = value.charAt(i);
+ chr = parseInt( chr, 10 );
+
+ if( Lang.isNumber( chr ) ){
+ height = (height * 10) + chr;
+ } else {
+ break;
+ }
+ }
+
+ return height;
+ },
+
+
+ /**
+ * Validator applied to the icon attribute. Setting new value is not allowed if Accordion has been rendered.
+ *
+ * @method _validateIcon
+ * @param value {MIXED} the value for the icon attribute
+ * @return {Boolean}
+ * @protected
+ */
+ _validateIcon: function( value ) {
+ return !this.get(RENDERED) || value;
+ },
+
+
+ /**
+ * Validator applied to the nodeLabel attribute. Setting new value is not allowed if Accordion has been rendered.
+ *
+ * @method _validateNodeLabel
+ * @param value {MIXED} the value for the nodeLabel attribute
+ * @return {Boolean}
+ * @protected
+ */
+ _validateNodeLabel: function( value ) {
+ return !this.get(RENDERED) || value;
+ },
+
+
+ /**
+ * Validator applied to the iconsContainer attribute. Setting new value is not allowed if Accordion has been rendered.
+ *
+ * @method _validateIconsContainer
+ * @param value {MIXED} the value for the iconsContainer attribute
+ * @return {Boolean}
+ * @protected
+ */
+ _validateIconsContainer: function( value ) {
+ return !this.get(RENDERED) || value;
+ },
+
+
+ /**
+ * Validator applied to the iconExpanded attribute. Setting new value is not allowed if Accordion has been rendered.
+ *
+ * @method _validateIconExpanded
+ * @param value {MIXED} the value for the iconExpanded attribute
+ * @return {Boolean}
+ * @protected
+ */
+ _validateIconExpanded: function( value ) {
+ return !this.get(RENDERED) || value;
+ },
+
+
+ /**
+ * Validator applied to the iconAlwaysVisible attribute. Setting new value is not allowed if Accordion has been rendered.
+ *
+ * @method _validateIconAlwaysVisible
+ * @param value {MIXED} the value for the iconAlwaysVisible attribute
+ * @return {Boolean}
+ * @protected
+ */
+ _validateIconAlwaysVisible: function( value ) {
+ return !this.get(RENDERED) || value;
+ },
+
+
+ /**
+ * Validator applied to the iconClose attribute. Setting new value is not allowed if Accordion has been rendered.
+ *
+ * @method _validateIconClose
+ * @param value {MIXED} the value for the iconClose attribute
+ * @return {Boolean}
+ * @protected
+ */
+ _validateIconClose: function( value ) {
+ return !this.get(RENDERED) || value;
+ },
+
+
+ /**
+ * Setter applied to the input when updating the icon attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setIcon
+ * @param value {Node|HTMLElement|String} The icon element Node or selector
+ * @return {Node} The Node if found, null otherwise.
+ * @protected
+ */
+ _setIcon: function( value ){
+ return Y.one( value ) || null;
+ },
+
+
+ /**
+ * Setter applied to the input when updating the nodeLabel attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setNodeLabel
+ * @param value {Node|HTMLElement|String} The nodeLabel element Node or selector
+ * @return {Node} The Node if found, null otherwise.
+ * @protected
+ */
+ _setNodeLabel: function( value ){
+ return Y.one( value ) || null;
+ },
+
+
+ /**
+ * Setter applied to the input when updating the iconsContainer attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setIconsContainer
+ * @param value {Node|HTMLElement|String} The iconsContainer element Node or selector
+ * @return {Node} The Node if found, null otherwise.
+ * @protected
+ */
+ _setIconsContainer: function( value ){
+ return Y.one( value ) || null;
+ },
+
+
+ /**
+ * Setter applied to the input when updating the iconExpanded attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setIconExpanded
+ * @param value {Node|HTMLElement|String} The iconExpanded element Node or selector
+ * @return {Node} The Node if found, null otherwise.
+ * @protected
+ */
+ _setIconExpanded: function( value ){
+ return Y.one( value ) || null;
+ },
+
+
+ /**
+ * Setter applied to the input when updating the iconAlwaysVisible attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setIconAlwaysVisible
+ * @param value {Node|HTMLElement|String} The iconAlwaysVisible element Node or selector
+ * @return {Node} The Node if found, null otherwise.
+ * @protected
+ */
+ _setIconAlwaysVisible: function( value ){
+ return Y.one( value ) || null;
+ },
+
+
+ /**
+ * Setter applied to the input when updating the iconClose attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setIconClose
+ * @param value {Node|HTMLElement|String} The iconClose element Node or selector
+ * @return {Node} The Node if found, null otherwise.
+ * @protected
+ */
+ _setIconClose: function( value ){
+ return Y.one( value ) || null;
+ },
+
+
+ /**
+ * Overwrites Widget's _applyParser method in order to parse yuiConfig attribute before entering in HTML_PARSER attributes
+ *
+ * @method _applyParser
+ * @protected
+ * @param config {Object} User configuration object (will be populated with values from Node)
+ */
+ _applyParser : function(config) {
+ var srcNode;
+
+ srcNode = this.get( "srcNode" );
+
+ if( srcNode ){
+ this._parsedYUIConfig = srcNode.getAttribute( YUICONFIG );
+
+ if( this._parsedYUIConfig ){
+ this._parsedYUIConfig = JSON.parse( this._parsedYUIConfig );
+ }
+ }
+
+ Y.AccordionItem.superclass._applyParser.apply( this, arguments );
+
+ delete this._parsedYUIConfig;
+ },
+
+
+ /**
+ * Overwrites Y.WidgetStdMod fuction in order to resolve Widget 3.1 issue:<br>
+ * If CONTENT_TEMPLATE is null, in renderUI the result of the following code:
+ * <code>this.getStdModNode( Y.WidgetStdMod.HEADER );</code> is null.
+ * The same is with <code>this.getStdModNode( Y.WidgetStdMod.BODY );</code>.
+ *
+ * @method _findStdModSection
+ * @protected
+ * @param {String} section The section for which the render Node is to be found. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @return {Node} The rendered node for the given section, or null if not found.
+ */
+ _findStdModSection: function(section) {
+ return this.get("srcNode").one("> ." + Y.WidgetStdMod.SECTION_CLASS_NAMES[section]);
+ },
+
+ CONTENT_TEMPLATE : null
+}, {
+ /**
+ * Static property provides a string to identify the class.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ */
+ NAME : AccItemName,
+
+ /**
+ * Static property used to define the default attribute
+ * configuration for the Accordion.
+ *
+ * @property Accordion.ATTRS
+ * @type Object
+ * @static
+ */
+ ATTRS : {
+
+ /**
+ * @description The Node, representing item's icon
+ *
+ * @attribute icon
+ * @default null
+ * @type Node
+ */
+ icon: {
+ value: null,
+ validator: function( value ){
+ return this._validateIcon( value );
+ },
+ setter : function( value ) {
+ return this._setIcon( value );
+ }
+ },
+
+ /**
+ * @description The label of item
+ *
+ * @attribute label
+ * @default " "
+ * @type String
+ */
+ label: {
+ value: " ",
+ validator: Lang.isString
+ },
+
+ /**
+ * @description The node, which contains item's label
+ *
+ * @attribute nodeLabel
+ * @default null
+ * @type Node
+ */
+ nodeLabel: {
+ value: null,
+ validator: function( value ){
+ return this._validateNodeLabel( value );
+ },
+ setter : function( value ) {
+ return this._setNodeLabel( value );
+ }
+ },
+
+
+ /**
+ * @description The container of iconAlwaysVisible, iconExpanded and iconClose
+ *
+ * @attribute iconsContainer
+ * @default null
+ * @type Node
+ */
+ iconsContainer: {
+ value: null,
+ validator: function( value ){
+ return this._validateIconsContainer( value );
+ },
+ setter : function( value ) {
+ return this._setIconsContainer( value );
+ }
+ },
+
+ /**
+ * @description The Node, representing icon expanded
+ *
+ * @attribute iconExpanded
+ * @default null
+ * @type Node
+ */
+ iconExpanded: {
+ value: null,
+ validator: function( value ){
+ return this._validateIconExpanded( value );
+ },
+ setter : function( value ) {
+ return this._setIconExpanded( value );
+ }
+ },
+
+
+ /**
+ * @description The Node, representing icon always visible
+ *
+ * @attribute iconAlwaysVisible
+ * @default null
+ * @type Node
+ */
+ iconAlwaysVisible: {
+ value: null,
+ validator: function( value ){
+ return this._validateIconAlwaysVisible( value );
+ },
+ setter : function( value ) {
+ return this._setIconAlwaysVisible( value );
+ }
+ },
+
+
+ /**
+ * @description The Node, representing icon close, or null if the item is not closable
+ *
+ * @attribute iconClose
+ * @default null
+ * @type Node
+ */
+ iconClose: {
+ value: null,
+ validator: function( value ){
+ return this._validateIconClose( value );
+ },
+ setter : function( value ) {
+ return this._setIconClose( value );
+ }
+ },
+
+ /**
+ * @description Get/Set expanded status of the item
+ *
+ * @attribute expanded
+ * @default false
+ * @type Boolean
+ */
+ expanded: {
+ value: false,
+ validator: Lang.isBoolean
+ },
+
+ /**
+ * @description Describe the method, which will be used when expanding/collapsing
+ * the item. The value should be an object with at least one property ("method"):
+ * <dl>
+ * <dt>method</dt>
+ * <dd>The method can be one of these: "auto", "fixed" and "stretch"</dd>
+ * <dt>height</dt>
+ * <dd>Must be set only if method's value is "fixed"</dd>
+ * </dl>
+ *
+ * @attribute contentHeight
+ * @default auto
+ * @type Object
+ */
+ contentHeight: {
+ value: {
+ method: AUTO
+ },
+ validator: function( value ){
+ if( Lang.isObject( value ) ){
+ if( value.method === AUTO ){
+ return true;
+ } else if( value.method === STRETCH ){
+ return true;
+ } else if( value.method === FIXED && Lang.isNumber( value.height ) &&
+ value.height >= 0 ){
+ return true;
+ }
+ }
+
+ return false;
+ }
+ },
+
+ /**
+ * @description Get/Set always visible status of the item
+ *
+ * @attribute alwaysVisible
+ * @default false
+ * @type Boolean
+ */
+ alwaysVisible: {
+ value: false,
+ validator: Lang.isBoolean
+ },
+
+
+ /**
+ * @description Get/Set the animaton specific settings. By default there are no any settings.
+ * If set, they will overwrite Accordion's animation settings
+ *
+ * @attribute animation
+ * @default {}
+ * @type Object
+ */
+ animation: {
+ value: {},
+ validator: Lang.isObject
+ },
+
+ /**
+ * @description Provides client side string localization support.
+ *
+ * @attribute strings
+ * @default Object English messages
+ * @type Object
+ */
+ strings: {
+ value: {
+ title_always_visible_off: "Click to set always visible on",
+ title_always_visible_on: "Click to set always visible off",
+ title_iconexpanded_off: "Click to expand",
+ title_iconexpanded_on: "Click to collapse",
+ title_iconclose: "Click to close"
+ }
+ },
+
+ /**
+ * @description Boolean indicating that the item can be closed by user.
+ * If true, there will be placed close icon, otherwise not
+ *
+ * @attribute closable
+ * @default false
+ * @type Boolean
+ */
+ closable: {
+ value: false,
+ validator: Lang.isBoolean
+ }
+ },
+
+
+ /**
+ * Static Object hash used to capture existing markup for progressive
+ * enhancement. Keys correspond to config attribute names and values
+ * are selectors used to inspect the srcNode for an existing node
+ * structure.
+ *
+ * @property HTML_PARSER
+ * @type Object
+ * @protected
+ * @static
+ */
+ HTML_PARSER : {
+
+ icon: HEADER_SELECTOR_SUB + C_ICON,
+
+ label: function( srcNode ){
+ var node, labelSelector, yuiConfig, label;
+
+ yuiConfig = this._parsedYUIConfig;
+
+ if( yuiConfig && Lang.isValue( yuiConfig.label ) ){
+ return yuiConfig.label;
+ }
+
+ label = srcNode.getAttribute( "data-label" );
+
+ if( label ){
+ return label;
+ }
+
+ labelSelector = HEADER_SELECTOR_SUB + C_LABEL;
+ node = srcNode.one( labelSelector );
+
+ return (node) ? node.get( INNER_HTML ) : null;
+ },
+
+ nodeLabel: HEADER_SELECTOR_SUB + C_LABEL,
+
+ iconsContainer: HEADER_SELECTOR_SUB + C_ICONSCONTAINER,
+
+ iconAlwaysVisible: HEADER_SELECTOR_SUB + C_ICONALWAYSVISIBLE,
+
+ iconExpanded: HEADER_SELECTOR_SUB + C_ICONEXPANDED,
+
+ iconClose: HEADER_SELECTOR_SUB + C_ICONCLOSE,
+
+ expanded: function( srcNode ){
+ var yuiConfig, expanded;
+
+ yuiConfig = this._parsedYUIConfig;
+
+ if( yuiConfig && Lang.isBoolean( yuiConfig.expanded ) ){
+ return yuiConfig.expanded;
+ }
+
+ expanded = srcNode.getAttribute( "data-expanded" );
+
+ if( expanded ) {
+ return REGEX_TRUE.test( expanded );
+ }
+
+ return srcNode.hasClass( C_EXPANDED );
+ },
+
+ alwaysVisible: function( srcNode ){
+ var yuiConfig, alwaysVisible;
+
+ yuiConfig = this._parsedYUIConfig;
+
+ if( yuiConfig && Lang.isBoolean( yuiConfig.alwaysVisible ) ){
+ alwaysVisible = yuiConfig.alwaysVisible;
+ } else {
+ alwaysVisible = srcNode.getAttribute( "data-alwaysvisible" );
+
+ if( alwaysVisible ) {
+ alwaysVisible = REGEX_TRUE.test( alwaysVisible );
+ } else {
+ alwaysVisible = srcNode.hasClass( C_ALWAYSVISIBLE );
+ }
+ }
+
+ if( alwaysVisible ){
+ this.set( "expanded", true, {
+ internalCall: true
+ } );
+ }
+
+ return alwaysVisible;
+ },
+
+ closable: function( srcNode ){
+ var yuiConfig, closable;
+
+ yuiConfig = this._parsedYUIConfig;
+
+ if( yuiConfig && Lang.isBoolean( yuiConfig.closable ) ){
+ return yuiConfig.closable;
+ }
+
+ closable = srcNode.getAttribute( "data-closable" );
+
+ if( closable ) {
+ return REGEX_TRUE.test( closable );
+ }
+
+ return srcNode.hasClass( C_CLOSABLE );
+ },
+
+ contentHeight: function( srcNode ){
+ var contentHeightClass, classValue, height = 0, index, yuiConfig,
+ contentHeight;
+
+ yuiConfig = this._parsedYUIConfig;
+
+ if( yuiConfig && yuiConfig.contentHeight ){
+ return yuiConfig.contentHeight;
+ }
+
+ contentHeight = srcNode.getAttribute( "data-contentheight" );
+
+ if( REGEX_AUTO.test( contentHeight ) ){
+ return {
+ method: AUTO
+ };
+ } else if( REGEX_STRETCH.test( contentHeight ) ){
+ return {
+ method: STRETCH
+ };
+ } else if( REGEX_FIXED.test( contentHeight ) ){
+ height = this._extractFixedMethodValue( contentHeight );
+
+ return {
+ method: FIXED,
+ height: height
+ };
+ }
+
+
+ classValue = srcNode.get( CLASS_NAME );
+
+ contentHeightClass = C_CONTENTHEIGHT + '-';
+
+ index = classValue.indexOf( contentHeightClass, 0);
+
+ if( index >= 0 ){
+ index += contentHeightClass.length;
+
+ classValue = classValue.substring( index );
+
+ if( REGEX_AUTO.test( classValue ) ){
+ return {
+ method: AUTO
+ };
+ } else if( REGEX_STRETCH.test( classValue ) ){
+ return {
+ method: STRETCH
+ };
+ } else if( REGEX_FIXED.test( classValue ) ){
+ height = this._extractFixedMethodValue( classValue );
+
+ return {
+ method: FIXED,
+ height: height
+ };
+ }
+ }
+
+ return null;
+ }
+ },
+
+
+ /**
+ * The template HTML strings for each of header components.
+ * e.g.
+ * <pre>
+ * {
+ * icon : '<a class="yui3-accordion-item-icon"></a>',
+ * label: '<a href="#" class="yui3-accordion-item-label"></a>',
+ * iconsContainer: '<div class="yui3-accordion-item-icons"></div>',
+ * iconAlwaysVisible: '<a href="#" class="yui3-accordion-item-iconalwaysvisible"></a>',
+ * iconExpanded: '<a href="#" class="yui3-accordion-item-iconexpanded"></a>',
+ * iconClose: '<a href="#" class="yui3-accordion-item-iconclose yui3-accordion-item-iconclose-hidden"></a>'
+ * }
+ * </pre>
+ * @property WidgetStdMod.TEMPLATES
+ * @type Object
+ */
+ TEMPLATES : {
+ icon : '<a class="' + C_ICON + '"></a>',
+ label: '<a href="#" class="' + C_LABEL + '"></a>',
+ iconsContainer: '<div class="' + C_ICONSCONTAINER + '"></div>',
+ iconExpanded: ['<a href="#" class="', C_ICONEXPANDED, ' ', C_ICONEXPANDED_OFF, '"></a>'].join(''),
+ iconAlwaysVisible: ['<a href="#" class="', C_ICONALWAYSVISIBLE, ' ', C_ICONALWAYSVISIBLE_OFF, '"></a>'].join(''),
+ iconClose: ['<a href="#" class="', C_ICONCLOSE, ' ', C_ICONCLOSE_HIDDEN, '"></a>'].join('')
+ }
+
+});
+
+}());
+
+
+
+}, '@VERSION@' ,{optional:['dd-constrain', 'dd-proxy', 'dd-drop'], requires:['event', 'anim-easing', 'widget', 'widget-stdmod', 'json-parse']});
=== modified file 'utilities/lp-deps.py'
--- utilities/lp-deps.py 2010-08-10 09:32:38 +0000
+++ utilities/lp-deps.py 2011-02-15 11:27:17 +0000
@@ -26,6 +26,7 @@
(os.path.join('lib', 'lp', 'registry', 'javascript'), 'registry'),
(os.path.join('lib', 'lp', 'translations', 'javascript'), 'translations'),
(os.path.join('lib', 'lp', 'soyuz', 'javascript'), 'soyuz'),
+ (os.path.join('lib', 'lp', 'contrib', 'javascript', 'yui3-gallery', 'gallery-accordion'), 'contrib'),
]
ICING_ROOT = os.path.join(TOP, 'lib', 'canonical', 'launchpad', 'icing')
ICING_BUILD = os.path.join(ICING_ROOT, 'build')
@@ -48,6 +49,10 @@
# We don't want the tests to be included. If we want to nest the files in
# more folders though, this is where we change it.
for filename in os.listdir(full_dir):
+ # Some third-party JavaScript libraries may include pre-built -min and
+ # -debug files. Skip those.
+ if filename.endswith('-min.js') or filename.endswith('-debug.js'):
+ continue
if filename.endswith('.js'):
basename, nothing = filename.split('.js')
min_filename = basename + '-min.js'