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