src/cm/media/js/lib/yui/yui3-3.15.0/build/widget-buttons/widget-buttons.js
changeset 602 e16a97fb364a
equal deleted inserted replaced
601:d334a616c023 602:e16a97fb364a
       
     1 YUI.add('widget-buttons', function (Y, NAME) {
       
     2 
       
     3 /**
       
     4 Provides header/body/footer button support for Widgets that use the
       
     5 `WidgetStdMod` extension.
       
     6 
       
     7 @module widget-buttons
       
     8 @since 3.4.0
       
     9 **/
       
    10 
       
    11 var YArray  = Y.Array,
       
    12     YLang   = Y.Lang,
       
    13     YObject = Y.Object,
       
    14 
       
    15     ButtonPlugin = Y.Plugin.Button,
       
    16     Widget       = Y.Widget,
       
    17     WidgetStdMod = Y.WidgetStdMod,
       
    18 
       
    19     getClassName = Y.ClassNameManager.getClassName,
       
    20     isArray      = YLang.isArray,
       
    21     isNumber     = YLang.isNumber,
       
    22     isString     = YLang.isString,
       
    23     isValue      = YLang.isValue;
       
    24 
       
    25 // Utility to determine if an object is a Y.Node instance, even if it was
       
    26 // created in a different YUI sandbox.
       
    27 function isNode(node) {
       
    28     return !!node.getDOMNode;
       
    29 }
       
    30 
       
    31 /**
       
    32 Provides header/body/footer button support for Widgets that use the
       
    33 `WidgetStdMod` extension.
       
    34 
       
    35 This Widget extension makes it easy to declaratively configure a widget's
       
    36 buttons. It adds a `buttons` attribute along with button- accessor and mutator
       
    37 methods. All button nodes have the `Y.Plugin.Button` plugin applied.
       
    38 
       
    39 This extension also includes `HTML_PARSER` support to seed a widget's `buttons`
       
    40 from those which already exist in its DOM.
       
    41 
       
    42 @class WidgetButtons
       
    43 @extensionfor Widget
       
    44 @since 3.4.0
       
    45 **/
       
    46 function WidgetButtons() {
       
    47     // Has to be setup before the `initializer()`.
       
    48     this._buttonsHandles = {};
       
    49 }
       
    50 
       
    51 WidgetButtons.ATTRS = {
       
    52     /**
       
    53     Collection containing a widget's buttons.
       
    54 
       
    55     The collection is an Object which contains an Array of `Y.Node`s for every
       
    56     `WidgetStdMod` section (header, body, footer) which has one or more buttons.
       
    57     All button nodes have the `Y.Plugin.Button` plugin applied.
       
    58 
       
    59     This attribute is very flexible in the values it will accept. `buttons` can
       
    60     be specified as a single Array, or an Object of Arrays keyed to a particular
       
    61     section.
       
    62 
       
    63     All specified values will be normalized to this type of structure:
       
    64 
       
    65         {
       
    66             header: [...],
       
    67             footer: [...]
       
    68         }
       
    69 
       
    70     A button can be specified as a `Y.Node`, config Object, or String name for a
       
    71     predefined button on the `BUTTONS` prototype property. When a config Object
       
    72     is provided, it will be merged with any defaults provided by a button with
       
    73     the same `name` defined on the `BUTTONS` property.
       
    74 
       
    75     See `addButton()` for the detailed list of configuration properties.
       
    76 
       
    77     For convenience, a widget's buttons will always persist and remain rendered
       
    78     after header/body/footer content updates. Buttons should be removed by
       
    79     updating this attribute or using the `removeButton()` method.
       
    80 
       
    81     @example
       
    82         {
       
    83             // Uses predefined "close" button by string name.
       
    84             header: ['close'],
       
    85 
       
    86             footer: [
       
    87                 {
       
    88                     name  : 'cancel',
       
    89                     label : 'Cancel',
       
    90                     action: 'hide'
       
    91                 },
       
    92 
       
    93                 {
       
    94                     name     : 'okay',
       
    95                     label    : 'Okay',
       
    96                     isDefault: true,
       
    97 
       
    98                     events: {
       
    99                         click: function (e) {
       
   100                             this.hide();
       
   101                         }
       
   102                     }
       
   103                 }
       
   104             ]
       
   105         }
       
   106 
       
   107     @attribute buttons
       
   108     @type Object
       
   109     @default {}
       
   110     @since 3.4.0
       
   111     **/
       
   112     buttons: {
       
   113         getter: '_getButtons',
       
   114         setter: '_setButtons',
       
   115         value : {}
       
   116     },
       
   117 
       
   118     /**
       
   119     The current default button as configured through this widget's `buttons`.
       
   120 
       
   121     A button can be configured as the default button in the following ways:
       
   122 
       
   123       * As a config Object with an `isDefault` property:
       
   124         `{label: 'Okay', isDefault: true}`.
       
   125 
       
   126       * As a Node with a `data-default` attribute:
       
   127         `<button data-default="true">Okay</button>`.
       
   128 
       
   129     This attribute is **read-only**; anytime there are changes to this widget's
       
   130     `buttons`, the `defaultButton` will be updated if needed.
       
   131 
       
   132     **Note:** If two or more buttons are configured to be the default button,
       
   133     the last one wins.
       
   134 
       
   135     @attribute defaultButton
       
   136     @type Node
       
   137     @default null
       
   138     @readOnly
       
   139     @since 3.5.0
       
   140     **/
       
   141     defaultButton: {
       
   142         readOnly: true,
       
   143         value   : null
       
   144     }
       
   145 };
       
   146 
       
   147 /**
       
   148 CSS classes used by `WidgetButtons`.
       
   149 
       
   150 @property CLASS_NAMES
       
   151 @type Object
       
   152 @static
       
   153 @since 3.5.0
       
   154 **/
       
   155 WidgetButtons.CLASS_NAMES = {
       
   156     button : getClassName('button'),
       
   157     buttons: Widget.getClassName('buttons'),
       
   158     primary: getClassName('button', 'primary')
       
   159 };
       
   160 
       
   161 WidgetButtons.HTML_PARSER = {
       
   162     buttons: function (srcNode) {
       
   163         return this._parseButtons(srcNode);
       
   164     }
       
   165 };
       
   166 
       
   167 /**
       
   168 The list of button configuration properties which are specific to
       
   169 `WidgetButtons` and should not be passed to `Y.Plugin.Button.createNode()`.
       
   170 
       
   171 @property NON_BUTTON_NODE_CFG
       
   172 @type Array
       
   173 @static
       
   174 @since 3.5.0
       
   175 **/
       
   176 WidgetButtons.NON_BUTTON_NODE_CFG = [
       
   177     'action', 'classNames', 'context', 'events', 'isDefault', 'section'
       
   178 ];
       
   179 
       
   180 WidgetButtons.prototype = {
       
   181     // -- Public Properties ----------------------------------------------------
       
   182 
       
   183     /**
       
   184     Collection of predefined buttons mapped by name -> config.
       
   185 
       
   186     These button configurations will serve as defaults for any button added to a
       
   187     widget's buttons which have the same `name`.
       
   188 
       
   189     See `addButton()` for a list of possible configuration values.
       
   190 
       
   191     @property BUTTONS
       
   192     @type Object
       
   193     @default {}
       
   194     @see addButton()
       
   195     @since 3.5.0
       
   196     **/
       
   197     BUTTONS: {},
       
   198 
       
   199     /**
       
   200     The HTML template to use when creating the node which wraps all buttons of a
       
   201     section. By default it will have the CSS class: "yui3-widget-buttons".
       
   202 
       
   203     @property BUTTONS_TEMPLATE
       
   204     @type String
       
   205     @default "<span />"
       
   206     @since 3.5.0
       
   207     **/
       
   208     BUTTONS_TEMPLATE: '<span />',
       
   209 
       
   210     /**
       
   211     The default section to render buttons in when no section is specified.
       
   212 
       
   213     @property DEFAULT_BUTTONS_SECTION
       
   214     @type String
       
   215     @default Y.WidgetStdMod.FOOTER
       
   216     @since 3.5.0
       
   217     **/
       
   218     DEFAULT_BUTTONS_SECTION: WidgetStdMod.FOOTER,
       
   219 
       
   220     // -- Protected Properties -------------------------------------------------
       
   221 
       
   222     /**
       
   223     A map of button node `_yuid` -> event-handle for all button nodes which were
       
   224     created by this widget.
       
   225 
       
   226     @property _buttonsHandles
       
   227     @type Object
       
   228     @protected
       
   229     @since 3.5.0
       
   230     **/
       
   231 
       
   232     /**
       
   233     A map of this widget's `buttons`, both name -> button and
       
   234     section:name -> button.
       
   235 
       
   236     @property _buttonsMap
       
   237     @type Object
       
   238     @protected
       
   239     @since 3.5.0
       
   240     **/
       
   241 
       
   242     /**
       
   243     Internal reference to this widget's default button.
       
   244 
       
   245     @property _defaultButton
       
   246     @type Node
       
   247     @protected
       
   248     @since 3.5.0
       
   249     **/
       
   250 
       
   251     // -- Lifecycle Methods ----------------------------------------------------
       
   252 
       
   253     initializer: function () {
       
   254         // Require `Y.WidgetStdMod`.
       
   255         if (!this._stdModNode) {
       
   256             Y.error('WidgetStdMod must be added to a Widget before WidgetButtons.');
       
   257         }
       
   258 
       
   259         // Creates button mappings and sets the `defaultButton`.
       
   260         this._mapButtons(this.get('buttons'));
       
   261         this._updateDefaultButton();
       
   262 
       
   263         // Bound with `Y.bind()` to make more extensible.
       
   264         this.after({
       
   265             buttonsChange      : Y.bind('_afterButtonsChange', this),
       
   266             defaultButtonChange: Y.bind('_afterDefaultButtonChange', this)
       
   267         });
       
   268 
       
   269         Y.after(this._bindUIButtons, this, 'bindUI');
       
   270         Y.after(this._syncUIButtons, this, 'syncUI');
       
   271     },
       
   272 
       
   273     destructor: function () {
       
   274         // Detach all event subscriptions this widget added to its `buttons`.
       
   275         YObject.each(this._buttonsHandles, function (handle) {
       
   276             handle.detach();
       
   277         });
       
   278 
       
   279         delete this._buttonsHandles;
       
   280         delete this._buttonsMap;
       
   281         delete this._defaultButton;
       
   282     },
       
   283 
       
   284     // -- Public Methods -------------------------------------------------------
       
   285 
       
   286     /**
       
   287     Adds a button to this widget.
       
   288 
       
   289     The new button node will have the `Y.Plugin.Button` plugin applied, be added
       
   290     to this widget's `buttons`, and rendered in the specified `section` at the
       
   291     specified `index` (or end of the section when no `index` is provided). If
       
   292     the section does not exist, it will be created.
       
   293 
       
   294     This fires the `buttonsChange` event and adds the following properties to
       
   295     the event facade:
       
   296 
       
   297       * `button`: The button node or config object to add.
       
   298 
       
   299       * `section`: The `WidgetStdMod` section (header/body/footer) where the
       
   300         button will be added.
       
   301 
       
   302       * `index`: The index at which the button will be in the section.
       
   303 
       
   304       * `src`: "add"
       
   305 
       
   306     **Note:** The `index` argument will be passed to the Array `splice()`
       
   307     method, therefore a negative value will insert the `button` that many items
       
   308     from the end. The `index` property on the `buttonsChange` event facade is
       
   309     the index at which the `button` was added.
       
   310 
       
   311     @method addButton
       
   312     @param {Node|Object|String} button The button to add. This can be a `Y.Node`
       
   313         instance, config Object, or String name for a predefined button on the
       
   314         `BUTTONS` prototype property. When a config Object is provided, it will
       
   315         be merged with any defaults provided by any `srcNode` and/or a button
       
   316         with the same `name` defined on the `BUTTONS` property. The following
       
   317         are the possible configuration properties beyond what Node plugins
       
   318         accept by default:
       
   319       @param {Function|String} [button.action] The default handler that should
       
   320         be called when the button is clicked. A String name of a Function that
       
   321         exists on the `context` object can also be provided. **Note:**
       
   322         Specifying a set of `events` will override this setting.
       
   323       @param {String|String[]} [button.classNames] Additional CSS classes to add
       
   324         to the button node.
       
   325       @param {Object} [button.context=this] Context which any `events` or
       
   326         `action` should be called with. Defaults to `this`, the widget.
       
   327         **Note:** `e.target` will access the button node in the event handlers.
       
   328       @param {Boolean} [button.disabled=false] Whether the button should be
       
   329         disabled.
       
   330       @param {String|Object} [button.events="click"] Event name, or set of
       
   331         events and handlers to bind to the button node. **See:** `Y.Node.on()`,
       
   332         this value is passed as the first argument to `on()`.
       
   333       @param {Boolean} [button.isDefault=false] Whether the button is the
       
   334         default button.
       
   335       @param {String} [button.label] The visible text/value displayed in the
       
   336         button.
       
   337       @param {String} [button.name] A name which can later be used to reference
       
   338         this button. If a button is defined on the `BUTTONS` property with this
       
   339         same name, its configuration properties will be merged in as defaults.
       
   340       @param {String} [button.section] The `WidgetStdMod` section (header, body,
       
   341         footer) where the button should be added.
       
   342       @param {Node} [button.srcNode] An existing Node to use for the button,
       
   343         default values will be seeded from this node, but are overriden by any
       
   344         values specified in the config object. By default a new &lt;button&gt;
       
   345         node will be created.
       
   346       @param {String} [button.template] A specific template to use when creating
       
   347         a new button node (e.g. "&lt;a /&gt;"). **Note:** Specifying a `srcNode`
       
   348         will overide this.
       
   349     @param {String} [section="footer"] The `WidgetStdMod` section
       
   350         (header/body/footer) where the button should be added. This takes
       
   351         precedence over the `button.section` configuration property.
       
   352     @param {Number} [index] The index at which the button should be inserted. If
       
   353         not specified, the button will be added to the end of the section. This
       
   354         value is passed to the Array `splice()` method, therefore a negative
       
   355         value will insert the `button` that many items from the end.
       
   356     @chainable
       
   357     @see Plugin.Button.createNode()
       
   358     @since 3.4.0
       
   359     **/
       
   360     addButton: function (button, section, index) {
       
   361         var buttons = this.get('buttons'),
       
   362             sectionButtons, atIndex;
       
   363 
       
   364         // Makes sure we have the full config object.
       
   365         if (!isNode(button)) {
       
   366             button = this._mergeButtonConfig(button);
       
   367             section || (section = button.section);
       
   368         }
       
   369 
       
   370         section || (section = this.DEFAULT_BUTTONS_SECTION);
       
   371         sectionButtons = buttons[section] || (buttons[section] = []);
       
   372         isNumber(index) || (index = sectionButtons.length);
       
   373 
       
   374         // Insert new button at the correct position.
       
   375         sectionButtons.splice(index, 0, button);
       
   376 
       
   377         // Determine the index at which the `button` now exists in the array.
       
   378         atIndex = YArray.indexOf(sectionButtons, button);
       
   379 
       
   380         this.set('buttons', buttons, {
       
   381             button : button,
       
   382             section: section,
       
   383             index  : atIndex,
       
   384             src    : 'add'
       
   385         });
       
   386 
       
   387         return this;
       
   388     },
       
   389 
       
   390     /**
       
   391     Returns a button node from this widget's `buttons`.
       
   392 
       
   393     @method getButton
       
   394     @param {Number|String} name The string name or index of the button.
       
   395     @param {String} [section="footer"] The `WidgetStdMod` section
       
   396         (header/body/footer) where the button exists. Only applicable when
       
   397         looking for a button by numerical index, or by name but scoped to a
       
   398         particular section.
       
   399     @return {Node} The button node.
       
   400     @since 3.5.0
       
   401     **/
       
   402     getButton: function (name, section) {
       
   403         if (!isValue(name)) { return; }
       
   404 
       
   405         var map = this._buttonsMap,
       
   406             buttons;
       
   407 
       
   408         section || (section = this.DEFAULT_BUTTONS_SECTION);
       
   409 
       
   410         // Supports `getButton(1, 'header')` signature.
       
   411         if (isNumber(name)) {
       
   412             buttons = this.get('buttons');
       
   413             return buttons[section] && buttons[section][name];
       
   414         }
       
   415 
       
   416         // Looks up button by name or section:name.
       
   417         return arguments.length > 1 ? map[section + ':' + name] : map[name];
       
   418     },
       
   419 
       
   420     /**
       
   421     Removes a button from this widget.
       
   422 
       
   423     The button will be removed from this widget's `buttons` and its DOM. Any
       
   424     event subscriptions on the button which were created by this widget will be
       
   425     detached. If the content section becomes empty after removing the button
       
   426     node, then the section will also be removed.
       
   427 
       
   428     This fires the `buttonsChange` event and adds the following properties to
       
   429     the event facade:
       
   430 
       
   431       * `button`: The button node to remove.
       
   432 
       
   433       * `section`: The `WidgetStdMod` section (header/body/footer) where the
       
   434         button should be removed from.
       
   435 
       
   436       * `index`: The index at which the button exists in the section.
       
   437 
       
   438       * `src`: "remove"
       
   439 
       
   440     @method removeButton
       
   441     @param {Node|Number|String} button The button to remove. This can be a
       
   442         `Y.Node` instance, index, or String name of a button.
       
   443     @param {String} [section="footer"] The `WidgetStdMod` section
       
   444         (header/body/footer) where the button exists. Only applicable when
       
   445         removing a button by numerical index, or by name but scoped to a
       
   446         particular section.
       
   447     @chainable
       
   448     @since 3.5.0
       
   449     **/
       
   450     removeButton: function (button, section) {
       
   451         if (!isValue(button)) { return this; }
       
   452 
       
   453         var buttons = this.get('buttons'),
       
   454             index;
       
   455 
       
   456         // Shortcut if `button` is already an index which is needed for slicing.
       
   457         if (isNumber(button)) {
       
   458             section || (section = this.DEFAULT_BUTTONS_SECTION);
       
   459             index  = button;
       
   460             button = buttons[section][index];
       
   461         } else {
       
   462             // Supports `button` being the string name.
       
   463             if (isString(button)) {
       
   464                 // `getButton()` is called this way because its behavior is
       
   465                 // different based on the number of arguments.
       
   466                 button = this.getButton.apply(this, arguments);
       
   467             }
       
   468 
       
   469             // Determines the `section` and `index` at which the button exists.
       
   470             YObject.some(buttons, function (sectionButtons, currentSection) {
       
   471                 index = YArray.indexOf(sectionButtons, button);
       
   472 
       
   473                 if (index > -1) {
       
   474                     section = currentSection;
       
   475                     return true;
       
   476                 }
       
   477             });
       
   478         }
       
   479 
       
   480         // Button was found at an appropriate index.
       
   481         if (button && index > -1) {
       
   482             // Remove button from `section` array.
       
   483             buttons[section].splice(index, 1);
       
   484 
       
   485             this.set('buttons', buttons, {
       
   486                 button : button,
       
   487                 section: section,
       
   488                 index  : index,
       
   489                 src    : 'remove'
       
   490             });
       
   491         }
       
   492 
       
   493         return this;
       
   494     },
       
   495 
       
   496     // -- Protected Methods ----------------------------------------------------
       
   497 
       
   498     /**
       
   499     Binds UI event listeners. This method is inserted via AOP, and will execute
       
   500     after `bindUI()`.
       
   501 
       
   502     @method _bindUIButtons
       
   503     @protected
       
   504     @since 3.4.0
       
   505     **/
       
   506     _bindUIButtons: function () {
       
   507         // Event handlers are bound with `bind()` to make them more extensible.
       
   508         var afterContentChange = Y.bind('_afterContentChangeButtons', this);
       
   509 
       
   510         this.after({
       
   511             visibleChange      : Y.bind('_afterVisibleChangeButtons', this),
       
   512             headerContentChange: afterContentChange,
       
   513             bodyContentChange  : afterContentChange,
       
   514             footerContentChange: afterContentChange
       
   515         });
       
   516     },
       
   517 
       
   518     /**
       
   519     Returns a button node based on the specified `button` node or configuration.
       
   520 
       
   521     The button node will either be created via `Y.Plugin.Button.createNode()`,
       
   522     or when `button` is specified as a node already, it will by `plug()`ed with
       
   523     `Y.Plugin.Button`.
       
   524 
       
   525     @method _createButton
       
   526     @param {Node|Object} button Button node or configuration object.
       
   527     @return {Node} The button node.
       
   528     @protected
       
   529     @since 3.5.0
       
   530     **/
       
   531     _createButton: function (button) {
       
   532         var config, buttonConfig, nonButtonNodeCfg,
       
   533             i, len, action, context, handle;
       
   534 
       
   535         // Makes sure the exiting `Y.Node` instance is from this YUI sandbox and
       
   536         // is plugged with `Y.Plugin.Button`.
       
   537         if (isNode(button)) {
       
   538             return Y.one(button.getDOMNode()).plug(ButtonPlugin);
       
   539         }
       
   540 
       
   541         // Merge `button` config with defaults and back-compat.
       
   542         config = Y.merge({
       
   543             context: this,
       
   544             events : 'click',
       
   545             label  : button.value
       
   546         }, button);
       
   547 
       
   548         buttonConfig     = Y.merge(config);
       
   549         nonButtonNodeCfg = WidgetButtons.NON_BUTTON_NODE_CFG;
       
   550 
       
   551         // Remove all non-button Node config props.
       
   552         for (i = 0, len = nonButtonNodeCfg.length; i < len; i += 1) {
       
   553             delete buttonConfig[nonButtonNodeCfg[i]];
       
   554         }
       
   555 
       
   556         // Create the button node using the button Node-only config.
       
   557         button = ButtonPlugin.createNode(buttonConfig);
       
   558 
       
   559         context = config.context;
       
   560         action  = config.action;
       
   561 
       
   562         // Supports `action` as a String name of a Function on the `context`
       
   563         // object.
       
   564         if (isString(action)) {
       
   565             action = Y.bind(action, context);
       
   566         }
       
   567 
       
   568         // Supports all types of crazy configs for event subscriptions and
       
   569         // stores a reference to the returned `EventHandle`.
       
   570         handle = button.on(config.events, action, context);
       
   571         this._buttonsHandles[Y.stamp(button, true)] = handle;
       
   572 
       
   573         // Tags the button with the configured `name` and `isDefault` settings.
       
   574         button.setData('name', this._getButtonName(config));
       
   575         button.setData('default', this._getButtonDefault(config));
       
   576 
       
   577         // Add any CSS classnames to the button node.
       
   578         YArray.each(YArray(config.classNames), button.addClass, button);
       
   579 
       
   580         return button;
       
   581     },
       
   582 
       
   583     /**
       
   584     Returns the buttons container for the specified `section`, passing a truthy
       
   585     value for `create` will create the node if it does not already exist.
       
   586 
       
   587     **Note:** It is up to the caller to properly insert the returned container
       
   588     node into the content section.
       
   589 
       
   590     @method _getButtonContainer
       
   591     @param {String} section The `WidgetStdMod` section (header/body/footer).
       
   592     @param {Boolean} create Whether the buttons container should be created if
       
   593         it does not already exist.
       
   594     @return {Node} The buttons container node for the specified `section`.
       
   595     @protected
       
   596     @see BUTTONS_TEMPLATE
       
   597     @since 3.5.0
       
   598     **/
       
   599     _getButtonContainer: function (section, create) {
       
   600         var sectionClassName = WidgetStdMod.SECTION_CLASS_NAMES[section],
       
   601             buttonsClassName = WidgetButtons.CLASS_NAMES.buttons,
       
   602             contentBox       = this.get('contentBox'),
       
   603             containerSelector, container;
       
   604 
       
   605         // Search for an existing buttons container within the section.
       
   606         containerSelector = '.' + sectionClassName + ' .' + buttonsClassName;
       
   607         container         = contentBox.one(containerSelector);
       
   608 
       
   609         // Create the `container` if it doesn't already exist.
       
   610         if (!container && create) {
       
   611             container = Y.Node.create(this.BUTTONS_TEMPLATE);
       
   612             container.addClass(buttonsClassName);
       
   613         }
       
   614 
       
   615         return container;
       
   616     },
       
   617 
       
   618     /**
       
   619     Returns whether or not the specified `button` is configured to be the
       
   620     default button.
       
   621 
       
   622     When a button node is specified, the button's `getData()` method will be
       
   623     used to determine if the button is configured to be the default. When a
       
   624     button config object is specified, the `isDefault` prop will determine
       
   625     whether the button is the default.
       
   626 
       
   627     **Note:** `<button data-default="true"></button>` is supported via the
       
   628     `button.getData('default')` API call.
       
   629 
       
   630     @method _getButtonDefault
       
   631     @param {Node|Object} button The button node or configuration object.
       
   632     @return {Boolean} Whether the button is configured to be the default button.
       
   633     @protected
       
   634     @since 3.5.0
       
   635     **/
       
   636     _getButtonDefault: function (button) {
       
   637         var isDefault = isNode(button) ?
       
   638                 button.getData('default') : button.isDefault;
       
   639 
       
   640         if (isString(isDefault)) {
       
   641             return isDefault.toLowerCase() === 'true';
       
   642         }
       
   643 
       
   644         return !!isDefault;
       
   645     },
       
   646 
       
   647     /**
       
   648     Returns the name of the specified `button`.
       
   649 
       
   650     When a button node is specified, the button's `getData('name')` method is
       
   651     preferred, but will fallback to `get('name')`, and the result will determine
       
   652     the button's name. When a button config object is specified, the `name` prop
       
   653     will determine the button's name.
       
   654 
       
   655     **Note:** `<button data-name="foo"></button>` is supported via the
       
   656     `button.getData('name')` API call.
       
   657 
       
   658     @method _getButtonName
       
   659     @param {Node|Object} button The button node or configuration object.
       
   660     @return {String} The name of the button.
       
   661     @protected
       
   662     @since 3.5.0
       
   663     **/
       
   664     _getButtonName: function (button) {
       
   665         var name;
       
   666 
       
   667         if (isNode(button)) {
       
   668             name = button.getData('name') || button.get('name');
       
   669         } else {
       
   670             name = button && (button.name || button.type);
       
   671         }
       
   672 
       
   673         return name;
       
   674     },
       
   675 
       
   676     /**
       
   677     Getter for the `buttons` attribute. A copy of the `buttons` object is
       
   678     returned so the stored state cannot be modified by the callers of
       
   679     `get('buttons')`.
       
   680 
       
   681     This will recreate a copy of the `buttons` object, and each section array
       
   682     (the button nodes are *not* copied/cloned.)
       
   683 
       
   684     @method _getButtons
       
   685     @param {Object} buttons The widget's current `buttons` state.
       
   686     @return {Object} A copy of the widget's current `buttons` state.
       
   687     @protected
       
   688     @since 3.5.0
       
   689     **/
       
   690     _getButtons: function (buttons) {
       
   691         var buttonsCopy = {};
       
   692 
       
   693         // Creates a new copy of the `buttons` object.
       
   694         YObject.each(buttons, function (sectionButtons, section) {
       
   695             // Creates of copy of the array of button nodes.
       
   696             buttonsCopy[section] = sectionButtons.concat();
       
   697         });
       
   698 
       
   699         return buttonsCopy;
       
   700     },
       
   701 
       
   702     /**
       
   703     Adds the specified `button` to the buttons map (both name -> button and
       
   704     section:name -> button), and sets the button as the default if it is
       
   705     configured as the default button.
       
   706 
       
   707     **Note:** If two or more buttons are configured with the same `name` and/or
       
   708     configured to be the default button, the last one wins.
       
   709 
       
   710     @method _mapButton
       
   711     @param {Node} button The button node to map.
       
   712     @param {String} section The `WidgetStdMod` section (header/body/footer).
       
   713     @protected
       
   714     @since 3.5.0
       
   715     **/
       
   716     _mapButton: function (button, section) {
       
   717         var map       = this._buttonsMap,
       
   718             name      = this._getButtonName(button),
       
   719             isDefault = this._getButtonDefault(button);
       
   720 
       
   721         if (name) {
       
   722             // name -> button
       
   723             map[name] = button;
       
   724 
       
   725             // section:name -> button
       
   726             map[section + ':' + name] = button;
       
   727         }
       
   728 
       
   729         isDefault && (this._defaultButton = button);
       
   730     },
       
   731 
       
   732     /**
       
   733     Adds the specified `buttons` to the buttons map (both name -> button and
       
   734     section:name -> button), and set the a button as the default if one is
       
   735     configured as the default button.
       
   736 
       
   737     **Note:** This will clear all previous button mappings and null-out any
       
   738     previous default button! If two or more buttons are configured with the same
       
   739     `name` and/or configured to be the default button, the last one wins.
       
   740 
       
   741     @method _mapButtons
       
   742     @param {Node[]} buttons The button nodes to map.
       
   743     @protected
       
   744     @since 3.5.0
       
   745     **/
       
   746     _mapButtons: function (buttons) {
       
   747         this._buttonsMap    = {};
       
   748         this._defaultButton = null;
       
   749 
       
   750         YObject.each(buttons, function (sectionButtons, section) {
       
   751             var i, len;
       
   752 
       
   753             for (i = 0, len = sectionButtons.length; i < len; i += 1) {
       
   754                 this._mapButton(sectionButtons[i], section);
       
   755             }
       
   756         }, this);
       
   757     },
       
   758 
       
   759     /**
       
   760     Returns a copy of the specified `config` object merged with any defaults
       
   761     provided by a `srcNode` and/or a predefined configuration for a button
       
   762     with the same `name` on the `BUTTONS` property.
       
   763 
       
   764     @method _mergeButtonConfig
       
   765     @param {Object|String} config Button configuration object, or string name.
       
   766     @return {Object} A copy of the button configuration object merged with any
       
   767         defaults.
       
   768     @protected
       
   769     @since 3.5.0
       
   770     **/
       
   771     _mergeButtonConfig: function (config) {
       
   772         var buttonConfig, defConfig, name, button, tagName, label;
       
   773 
       
   774         // Makes sure `config` is an Object and a copy of the specified value.
       
   775         config = isString(config) ? {name: config} : Y.merge(config);
       
   776 
       
   777         // Seeds default values from the button node, if there is one.
       
   778         if (config.srcNode) {
       
   779             button  = config.srcNode;
       
   780             tagName = button.get('tagName').toLowerCase();
       
   781             label   = button.get(tagName === 'input' ? 'value' : 'text');
       
   782 
       
   783             // Makes sure the button's current values override any defaults.
       
   784             buttonConfig = {
       
   785                 disabled : !!button.get('disabled'),
       
   786                 isDefault: this._getButtonDefault(button),
       
   787                 name     : this._getButtonName(button)
       
   788             };
       
   789 
       
   790             // Label should only be considered when not an empty string.
       
   791             label && (buttonConfig.label = label);
       
   792 
       
   793             // Merge `config` with `buttonConfig` values.
       
   794             Y.mix(config, buttonConfig, false, null, 0, true);
       
   795         }
       
   796 
       
   797         name      = this._getButtonName(config);
       
   798         defConfig = this.BUTTONS && this.BUTTONS[name];
       
   799 
       
   800         // Merge `config` with predefined default values.
       
   801         if (defConfig) {
       
   802             Y.mix(config, defConfig, false, null, 0, true);
       
   803         }
       
   804 
       
   805         return config;
       
   806     },
       
   807 
       
   808     /**
       
   809     `HTML_PARSER` implementation for the `buttons` attribute.
       
   810 
       
   811     **Note:** To determine a button node's name its `data-name` and `name`
       
   812     attributes are examined. Whether the button should be the default is
       
   813     determined by its `data-default` attribute.
       
   814 
       
   815     @method _parseButtons
       
   816     @param {Node} srcNode This widget's srcNode to search for buttons.
       
   817     @return {null|Object} `buttons` Config object parsed from this widget's DOM.
       
   818     @protected
       
   819     @since 3.5.0
       
   820     **/
       
   821     _parseButtons: function (srcNode) {
       
   822         var buttonSelector = '.' + WidgetButtons.CLASS_NAMES.button,
       
   823             sections       = ['header', 'body', 'footer'],
       
   824             buttonsConfig  = null;
       
   825 
       
   826         YArray.each(sections, function (section) {
       
   827             var container = this._getButtonContainer(section),
       
   828                 buttons   = container && container.all(buttonSelector),
       
   829                 sectionButtons;
       
   830 
       
   831             if (!buttons || buttons.isEmpty()) { return; }
       
   832 
       
   833             sectionButtons = [];
       
   834 
       
   835             // Creates a button config object for every button node found and
       
   836             // adds it to the section. This way each button configuration can be
       
   837             // merged with any defaults provided by predefined `BUTTONS`.
       
   838             buttons.each(function (button) {
       
   839                 sectionButtons.push({srcNode: button});
       
   840             });
       
   841 
       
   842             buttonsConfig || (buttonsConfig = {});
       
   843             buttonsConfig[section] = sectionButtons;
       
   844         }, this);
       
   845 
       
   846         return buttonsConfig;
       
   847     },
       
   848 
       
   849     /**
       
   850     Setter for the `buttons` attribute. This processes the specified `config`
       
   851     and returns a new `buttons` object which is stored as the new state; leaving
       
   852     the original, specified `config` unmodified.
       
   853 
       
   854     The button nodes will either be created via `Y.Plugin.Button.createNode()`,
       
   855     or when a button is already a Node already, it will by `plug()`ed with
       
   856     `Y.Plugin.Button`.
       
   857 
       
   858     @method _setButtons
       
   859     @param {Array|Object} config The `buttons` configuration to process.
       
   860     @return {Object} The processed `buttons` object which represents the new
       
   861         state.
       
   862     @protected
       
   863     @since 3.5.0
       
   864     **/
       
   865     _setButtons: function (config) {
       
   866         var defSection = this.DEFAULT_BUTTONS_SECTION,
       
   867             buttons    = {};
       
   868 
       
   869         function processButtons(buttonConfigs, currentSection) {
       
   870             if (!isArray(buttonConfigs)) { return; }
       
   871 
       
   872             var i, len, button, section;
       
   873 
       
   874             for (i = 0, len = buttonConfigs.length; i < len; i += 1) {
       
   875                 button  = buttonConfigs[i];
       
   876                 section = currentSection;
       
   877 
       
   878                 if (!isNode(button)) {
       
   879                     button = this._mergeButtonConfig(button);
       
   880                     section || (section = button.section);
       
   881                 }
       
   882 
       
   883                 // Always passes through `_createButton()` to make sure the node
       
   884                 // is decorated as a button.
       
   885                 button = this._createButton(button);
       
   886 
       
   887                 // Use provided `section` or fallback to the default section.
       
   888                 section || (section = defSection);
       
   889 
       
   890                 // Add button to the array of buttons for the specified section.
       
   891                 (buttons[section] || (buttons[section] = [])).push(button);
       
   892             }
       
   893         }
       
   894 
       
   895         // Handle `config` being either an Array or Object of Arrays.
       
   896         if (isArray(config)) {
       
   897             processButtons.call(this, config);
       
   898         } else {
       
   899             YObject.each(config, processButtons, this);
       
   900         }
       
   901 
       
   902         return buttons;
       
   903     },
       
   904 
       
   905     /**
       
   906     Syncs this widget's current button-related state to its DOM. This method is
       
   907     inserted via AOP, and will execute after `syncUI()`.
       
   908 
       
   909     @method _syncUIButtons
       
   910     @protected
       
   911     @since 3.4.0
       
   912     **/
       
   913     _syncUIButtons: function () {
       
   914         this._uiSetButtons(this.get('buttons'));
       
   915         this._uiSetDefaultButton(this.get('defaultButton'));
       
   916         this._uiSetVisibleButtons(this.get('visible'));
       
   917     },
       
   918 
       
   919     /**
       
   920     Inserts the specified `button` node into this widget's DOM at the specified
       
   921     `section` and `index` and updates the section content.
       
   922 
       
   923     The section and button container nodes will be created if they do not
       
   924     already exist.
       
   925 
       
   926     @method _uiInsertButton
       
   927     @param {Node} button The button node to insert into this widget's DOM.
       
   928     @param {String} section The `WidgetStdMod` section (header/body/footer).
       
   929     @param {Number} index Index at which the `button` should be positioned.
       
   930     @protected
       
   931     @since 3.5.0
       
   932     **/
       
   933     _uiInsertButton: function (button, section, index) {
       
   934         var buttonsClassName = WidgetButtons.CLASS_NAMES.button,
       
   935             buttonContainer  = this._getButtonContainer(section, true),
       
   936             sectionButtons   = buttonContainer.all('.' + buttonsClassName);
       
   937 
       
   938         // Inserts the button node at the correct index.
       
   939         buttonContainer.insertBefore(button, sectionButtons.item(index));
       
   940 
       
   941         // Adds the button container to the section content.
       
   942         this.setStdModContent(section, buttonContainer, 'after');
       
   943     },
       
   944 
       
   945     /**
       
   946     Removes the button node from this widget's DOM and detaches any event
       
   947     subscriptions on the button that were created by this widget. The section
       
   948     content will be updated unless `{preserveContent: true}` is passed in the
       
   949     `options`.
       
   950 
       
   951     By default the button container node will be removed when this removes the
       
   952     last button of the specified `section`; and if no other content remains in
       
   953     the section node, it will also be removed.
       
   954 
       
   955     @method _uiRemoveButton
       
   956     @param {Node} button The button to remove and destroy.
       
   957     @param {String} section The `WidgetStdMod` section (header/body/footer).
       
   958     @param {Object} [options] Additional options.
       
   959       @param {Boolean} [options.preserveContent=false] Whether the section
       
   960         content should be updated.
       
   961     @protected
       
   962     @since 3.5.0
       
   963     **/
       
   964     _uiRemoveButton: function (button, section, options) {
       
   965         var yuid    = Y.stamp(button, this),
       
   966             handles = this._buttonsHandles,
       
   967             handle  = handles[yuid],
       
   968             buttonContainer, buttonClassName;
       
   969 
       
   970         if (handle) {
       
   971             handle.detach();
       
   972         }
       
   973 
       
   974         delete handles[yuid];
       
   975 
       
   976         button.remove();
       
   977 
       
   978         options || (options = {});
       
   979 
       
   980         // Remove the button container and section nodes if needed.
       
   981         if (!options.preserveContent) {
       
   982             buttonContainer = this._getButtonContainer(section);
       
   983             buttonClassName = WidgetButtons.CLASS_NAMES.button;
       
   984 
       
   985             // Only matters if we have a button container which is empty.
       
   986             if (buttonContainer &&
       
   987                     buttonContainer.all('.' + buttonClassName).isEmpty()) {
       
   988 
       
   989                 buttonContainer.remove();
       
   990                 this._updateContentButtons(section);
       
   991             }
       
   992         }
       
   993     },
       
   994 
       
   995     /**
       
   996     Sets the current `buttons` state to this widget's DOM by rendering the
       
   997     specified collection of `buttons` and updates the contents of each section
       
   998     as needed.
       
   999 
       
  1000     Button nodes which already exist in the DOM will remain intact, or will be
       
  1001     moved if they should be in a new position. Old button nodes which are no
       
  1002     longer represented in the specified `buttons` collection will be removed,
       
  1003     and any event subscriptions on the button which were created by this widget
       
  1004     will be detached.
       
  1005 
       
  1006     If the button nodes in this widget's DOM actually change, then each content
       
  1007     section will be updated (or removed) appropriately.
       
  1008 
       
  1009     @method _uiSetButtons
       
  1010     @param {Object} buttons The current `buttons` state to visually represent.
       
  1011     @protected
       
  1012     @since 3.5.0
       
  1013     **/
       
  1014     _uiSetButtons: function (buttons) {
       
  1015         var buttonClassName = WidgetButtons.CLASS_NAMES.button,
       
  1016             sections        = ['header', 'body', 'footer'];
       
  1017 
       
  1018         YArray.each(sections, function (section) {
       
  1019             var sectionButtons  = buttons[section] || [],
       
  1020                 numButtons      = sectionButtons.length,
       
  1021                 buttonContainer = this._getButtonContainer(section, numButtons),
       
  1022                 buttonsUpdated  = false,
       
  1023                 oldNodes, i, button, buttonIndex;
       
  1024 
       
  1025             // When there's no button container, there are no new buttons or old
       
  1026             // buttons that we have to deal with for this section.
       
  1027             if (!buttonContainer) { return; }
       
  1028 
       
  1029             oldNodes = buttonContainer.all('.' + buttonClassName);
       
  1030 
       
  1031             for (i = 0; i < numButtons; i += 1) {
       
  1032                 button      = sectionButtons[i];
       
  1033                 buttonIndex = oldNodes.indexOf(button);
       
  1034 
       
  1035                 // Buttons already rendered in the Widget should remain there or
       
  1036                 // moved to their new index. New buttons will be added to the
       
  1037                 // current `buttonContainer`.
       
  1038                 if (buttonIndex > -1) {
       
  1039                     // Remove button from existing buttons nodeList since its in
       
  1040                     // the DOM already.
       
  1041                     oldNodes.splice(buttonIndex, 1);
       
  1042 
       
  1043                     // Check that the button is at the right position, if not,
       
  1044                     // move it to its new position.
       
  1045                     if (buttonIndex !== i) {
       
  1046                         // Using `i + 1` because the button should be at index
       
  1047                         // `i`; it's inserted before the node which comes after.
       
  1048                         buttonContainer.insertBefore(button, i + 1);
       
  1049                         buttonsUpdated = true;
       
  1050                     }
       
  1051                 } else {
       
  1052                     buttonContainer.appendChild(button);
       
  1053                     buttonsUpdated = true;
       
  1054                 }
       
  1055             }
       
  1056 
       
  1057             // Safely removes the old button nodes which are no longer part of
       
  1058             // this widget's `buttons`.
       
  1059             oldNodes.each(function (button) {
       
  1060                 this._uiRemoveButton(button, section, {preserveContent: true});
       
  1061                 buttonsUpdated = true;
       
  1062             }, this);
       
  1063 
       
  1064             // Remove leftover empty button containers and updated the StdMod
       
  1065             // content area.
       
  1066             if (numButtons === 0) {
       
  1067                 buttonContainer.remove();
       
  1068                 this._updateContentButtons(section);
       
  1069                 return;
       
  1070             }
       
  1071 
       
  1072             // Adds the button container to the section content.
       
  1073             if (buttonsUpdated) {
       
  1074                 this.setStdModContent(section, buttonContainer, 'after');
       
  1075             }
       
  1076         }, this);
       
  1077     },
       
  1078 
       
  1079     /**
       
  1080     Adds the "yui3-button-primary" CSS class to the new `defaultButton` and
       
  1081     removes it from the old default button.
       
  1082 
       
  1083     @method _uiSetDefaultButton
       
  1084     @param {Node} newButton The new `defaultButton`.
       
  1085     @param {Node} oldButton The old `defaultButton`.
       
  1086     @protected
       
  1087     @since 3.5.0
       
  1088     **/
       
  1089     _uiSetDefaultButton: function (newButton, oldButton) {
       
  1090         var primaryClassName = WidgetButtons.CLASS_NAMES.primary;
       
  1091 
       
  1092         if (newButton) { newButton.addClass(primaryClassName); }
       
  1093         if (oldButton) { oldButton.removeClass(primaryClassName); }
       
  1094     },
       
  1095 
       
  1096     /**
       
  1097     Focuses this widget's `defaultButton` if there is one and this widget is
       
  1098     visible.
       
  1099 
       
  1100     @method _uiSetVisibleButtons
       
  1101     @param {Boolean} visible Whether this widget is visible.
       
  1102     @protected
       
  1103     @since 3.5.0
       
  1104     **/
       
  1105     _uiSetVisibleButtons: function (visible) {
       
  1106         if (!visible) { return; }
       
  1107 
       
  1108         var defaultButton = this.get('defaultButton');
       
  1109         if (defaultButton) {
       
  1110             defaultButton.focus();
       
  1111         }
       
  1112     },
       
  1113 
       
  1114     /**
       
  1115     Removes the specified `button` from the buttons map (both name -> button and
       
  1116     section:name -> button), and nulls-out the `defaultButton` if it is
       
  1117     currently the default button.
       
  1118 
       
  1119     @method _unMapButton
       
  1120     @param {Node} button The button node to remove from the buttons map.
       
  1121     @param {String} section The `WidgetStdMod` section (header/body/footer).
       
  1122     @protected
       
  1123     @since 3.5.0
       
  1124     **/
       
  1125     _unMapButton: function (button, section) {
       
  1126         var map  = this._buttonsMap,
       
  1127             name = this._getButtonName(button),
       
  1128             sectionName;
       
  1129 
       
  1130         // Only delete the map entry if the specified `button` is mapped to it.
       
  1131         if (name) {
       
  1132             // name -> button
       
  1133             if (map[name] === button) {
       
  1134                 delete map[name];
       
  1135             }
       
  1136 
       
  1137             // section:name -> button
       
  1138             sectionName = section + ':' + name;
       
  1139             if (map[sectionName] === button) {
       
  1140                 delete map[sectionName];
       
  1141             }
       
  1142         }
       
  1143 
       
  1144         // Clear the default button if its the specified `button`.
       
  1145         if (this._defaultButton === button) {
       
  1146             this._defaultButton = null;
       
  1147         }
       
  1148     },
       
  1149 
       
  1150     /**
       
  1151     Updates the `defaultButton` attribute if it needs to be updated by comparing
       
  1152     its current value with the protected `_defaultButton` property.
       
  1153 
       
  1154     @method _updateDefaultButton
       
  1155     @protected
       
  1156     @since 3.5.0
       
  1157     **/
       
  1158     _updateDefaultButton: function () {
       
  1159         var defaultButton = this._defaultButton;
       
  1160 
       
  1161         if (this.get('defaultButton') !== defaultButton) {
       
  1162             this._set('defaultButton', defaultButton);
       
  1163         }
       
  1164     },
       
  1165 
       
  1166     /**
       
  1167     Updates the content attribute which corresponds to the specified `section`.
       
  1168 
       
  1169     The method updates the section's content to its current `childNodes`
       
  1170     (text and/or HTMLElement), or will null-out its contents if the section is
       
  1171     empty. It also specifies a `src` of `buttons` on the change event facade.
       
  1172 
       
  1173     @method _updateContentButtons
       
  1174     @param {String} section The `WidgetStdMod` section (header/body/footer) to
       
  1175         update.
       
  1176     @protected
       
  1177     @since 3.5.0
       
  1178     **/
       
  1179     _updateContentButtons: function (section) {
       
  1180         // `childNodes` return text nodes and HTMLElements.
       
  1181         var sectionContent = this.getStdModNode(section).get('childNodes');
       
  1182 
       
  1183         // Updates the section to its current contents, or null if it is empty.
       
  1184         this.set(section + 'Content', sectionContent.isEmpty() ? null :
       
  1185             sectionContent, {src: 'buttons'});
       
  1186     },
       
  1187 
       
  1188     // -- Protected Event Handlers ---------------------------------------------
       
  1189 
       
  1190     /**
       
  1191     Handles this widget's `buttonsChange` event which fires anytime the
       
  1192     `buttons` attribute is modified.
       
  1193 
       
  1194     **Note:** This method special-cases the `buttons` modifications caused by
       
  1195     `addButton()` and `removeButton()`, both of which set the `src` property on
       
  1196     the event facade to "add" and "remove" respectively.
       
  1197 
       
  1198     @method _afterButtonsChange
       
  1199     @param {EventFacade} e
       
  1200     @protected
       
  1201     @since 3.4.0
       
  1202     **/
       
  1203     _afterButtonsChange: function (e) {
       
  1204         var buttons = e.newVal,
       
  1205             section = e.section,
       
  1206             index   = e.index,
       
  1207             src     = e.src,
       
  1208             button;
       
  1209 
       
  1210         // Special cases `addButton()` to only set and insert the new button.
       
  1211         if (src === 'add') {
       
  1212             // Make sure we have the button node.
       
  1213             button = buttons[section][index];
       
  1214 
       
  1215             this._mapButton(button, section);
       
  1216             this._updateDefaultButton();
       
  1217             this._uiInsertButton(button, section, index);
       
  1218 
       
  1219             return;
       
  1220         }
       
  1221 
       
  1222         // Special cases `removeButton()` to only remove the specified button.
       
  1223         if (src === 'remove') {
       
  1224             // Button node already exists on the event facade.
       
  1225             button = e.button;
       
  1226 
       
  1227             this._unMapButton(button, section);
       
  1228             this._updateDefaultButton();
       
  1229             this._uiRemoveButton(button, section);
       
  1230 
       
  1231             return;
       
  1232         }
       
  1233 
       
  1234         this._mapButtons(buttons);
       
  1235         this._updateDefaultButton();
       
  1236         this._uiSetButtons(buttons);
       
  1237     },
       
  1238 
       
  1239     /**
       
  1240     Handles this widget's `headerContentChange`, `bodyContentChange`,
       
  1241     `footerContentChange` events by making sure the `buttons` remain rendered
       
  1242     after changes to the content areas.
       
  1243 
       
  1244     These events are very chatty, so extra caution is taken to avoid doing extra
       
  1245     work or getting into an infinite loop.
       
  1246 
       
  1247     @method _afterContentChangeButtons
       
  1248     @param {EventFacade} e
       
  1249     @protected
       
  1250     @since 3.5.0
       
  1251     **/
       
  1252     _afterContentChangeButtons: function (e) {
       
  1253         var src     = e.src,
       
  1254             pos     = e.stdModPosition,
       
  1255             replace = !pos || pos === WidgetStdMod.REPLACE;
       
  1256 
       
  1257         // Only do work when absolutely necessary.
       
  1258         if (replace && src !== 'buttons' && src !== Widget.UI_SRC) {
       
  1259             this._uiSetButtons(this.get('buttons'));
       
  1260         }
       
  1261     },
       
  1262 
       
  1263     /**
       
  1264     Handles this widget's `defaultButtonChange` event by adding the
       
  1265     "yui3-button-primary" CSS class to the new `defaultButton` and removing it
       
  1266     from the old default button.
       
  1267 
       
  1268     @method _afterDefaultButtonChange
       
  1269     @param {EventFacade} e
       
  1270     @protected
       
  1271     @since 3.5.0
       
  1272     **/
       
  1273     _afterDefaultButtonChange: function (e) {
       
  1274         this._uiSetDefaultButton(e.newVal, e.prevVal);
       
  1275     },
       
  1276 
       
  1277     /**
       
  1278     Handles this widget's `visibleChange` event by focusing the `defaultButton`
       
  1279     if there is one.
       
  1280 
       
  1281     @method _afterVisibleChangeButtons
       
  1282     @param {EventFacade} e
       
  1283     @protected
       
  1284     @since 3.5.0
       
  1285     **/
       
  1286     _afterVisibleChangeButtons: function (e) {
       
  1287         this._uiSetVisibleButtons(e.newVal);
       
  1288     }
       
  1289 };
       
  1290 
       
  1291 Y.WidgetButtons = WidgetButtons;
       
  1292 
       
  1293 
       
  1294 }, '@VERSION@', {"requires": ["button-plugin", "cssbutton", "widget-stdmod"]});