MDL-32843 import YUI 3.5.1
[moodle.git] / lib / yui / 3.5.1 / build / tabview / tabview.js
blob1e33d87323a0b99c5c3893622807d482e23b9317
1 /*
2 YUI 3.5.1 (build 22)
3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
7 YUI.add('tabview', function(Y) {
9 /**
10  * The TabView module 
11  *
12  * @module tabview
13  */
15 var _queries = Y.TabviewBase._queries,
16     _classNames = Y.TabviewBase._classNames,
17     DOT = '.',
18     getClassName = Y.ClassNameManager.getClassName,
20     /**
21      * Provides a tabbed widget interface 
22      * @param config {Object} Object literal specifying tabview configuration properties.
23      *
24      * @class TabView
25      * @constructor
26      * @extends Widget
27      * @uses WidgetParent
28      */
29     TabView = Y.Base.create('tabView', Y.Widget, [Y.WidgetParent], {
30     _afterChildAdded: function(e) {
31         this.get('contentBox').focusManager.refresh();
32     },
34     _defListNodeValueFn: function() {
35         return Y.Node.create(TabView.LIST_TEMPLATE);
36     },
38     _defPanelNodeValueFn: function() {
39         return Y.Node.create(TabView.PANEL_TEMPLATE);
40     },
42     _afterChildRemoved: function(e) { // update the selected tab when removed
43         var i = e.index,
44             selection = this.get('selection');
46         if (!selection) { // select previous item if selection removed
47             selection = this.item(i - 1) || this.item(0);
48             if (selection) {
49                 selection.set('selected', 1);
50             }
51         }
53         this.get('contentBox').focusManager.refresh();
54     },
56     _initAria: function() {
57         var contentBox = this.get('contentBox'),
58             tablist = contentBox.one(_queries.tabviewList);
60         if (tablist) {
61             tablist.setAttrs({
62                 //'aria-labelledby': 
63                 role: 'tablist'
64             });
65         }
66     },
68     bindUI: function() {
69         //  Use the Node Focus Manager to add keyboard support:
70         //  Pressing the left and right arrow keys will move focus
71         //  among each of the tabs.
73         this.get('contentBox').plug(Y.Plugin.NodeFocusManager, {
74                         descendants: DOT + _classNames.tabLabel,
75                         keys: { next: 'down:39', // Right arrow
76                                 previous: 'down:37' },  // Left arrow
77                         circular: true
78                     });
80         this.after('render', this._setDefSelection);
81         this.after('addChild', this._afterChildAdded);
82         this.after('removeChild', this._afterChildRemoved);
83     },
84     
85     renderUI: function() {
86         var contentBox = this.get('contentBox'); 
87         this._renderListBox(contentBox);
88         this._renderPanelBox(contentBox);
89         this._childrenContainer = this.get('listNode');
90         this._renderTabs(contentBox);
91     },
93     _setDefSelection: function(contentBox) {
94         //  If no tab is selected, select the first tab.
95         var selection = this.get('selection') || this.item(0);
97         this.some(function(tab) {
98             if (tab.get('selected')) {
99                 selection = tab;
100                 return true;
101             }
102         });
103         if (selection) {
104             // TODO: why both needed? (via widgetParent/Child)?
105             this.set('selection', selection);
106             selection.set('selected', 1);
107         }
108     },
110     _renderListBox: function(contentBox) {
111         var node = this.get('listNode');
112         if (!node.inDoc()) {
113             contentBox.append(node);
114         }
115     },
117     _renderPanelBox: function(contentBox) {
118         var node = this.get('panelNode');
119         if (!node.inDoc()) {
120             contentBox.append(node);
121         }
122     },
124     _renderTabs: function(contentBox) {
125         var tabs = contentBox.all(_queries.tab),
126             panelNode = this.get('panelNode'),
127             panels = (panelNode) ? this.get('panelNode').get('children') : null,
128             tabview = this;
130         if (tabs) { // add classNames and fill in Tab fields from markup when possible
131             tabs.addClass(_classNames.tab);
132             contentBox.all(_queries.tabLabel).addClass(_classNames.tabLabel);
133             contentBox.all(_queries.tabPanel).addClass(_classNames.tabPanel);
135             tabs.each(function(node, i) {
136                 var panelNode = (panels) ? panels.item(i) : null;
137                 tabview.add({
138                     boundingBox: node,
139                     contentBox: node.one(DOT + _classNames.tabLabel),
140                     label: node.one(DOT + _classNames.tabLabel).get('text'),
141                     panelNode: panelNode
142                 });
143             });
144         }
145     }
146 }, {
148     LIST_TEMPLATE: '<ul class="' + _classNames.tabviewList + '"></ul>',
149     PANEL_TEMPLATE: '<div class="' + _classNames.tabviewPanel + '"></div>',
151     ATTRS: {
152         defaultChildType: {  
153             value: 'Tab'
154         },
156         listNode: {
157             setter: function(node) {
158                 node = Y.one(node);
159                 if (node) {
160                     node.addClass(_classNames.tabviewList);
161                 }
162                 return node;
163             },
165             valueFn: '_defListNodeValueFn'
166         },
168         panelNode: {
169             setter: function(node) {
170                 node = Y.one(node);
171                 if (node) {
172                     node.addClass(_classNames.tabviewPanel);
173                 }
174                 return node;
175             },
177             valueFn: '_defPanelNodeValueFn'
178         },
180         tabIndex: {
181             value: null
182             //validator: '_validTabIndex'
183         }
184     },
186     HTML_PARSER: {
187         listNode: _queries.tabviewList,
188         panelNode: _queries.tabviewPanel
189     }
192 Y.TabView = TabView;
193 var Lang = Y.Lang,
194     _queries = Y.TabviewBase._queries,
195     _classNames = Y.TabviewBase._classNames,
196     getClassName = Y.ClassNameManager.getClassName;
199  * Provides Tab instances for use with TabView
200  * @param config {Object} Object literal specifying tabview configuration properties.
202  * @class Tab
203  * @constructor
204  * @extends Widget
205  * @uses WidgetChild
206  */
207 Y.Tab = Y.Base.create('tab', Y.Widget, [Y.WidgetChild], {
208     BOUNDING_TEMPLATE: '<li class="' + _classNames.tab + '"></li>',
209     CONTENT_TEMPLATE: '<a class="' + _classNames.tabLabel + '"></a>',
210     PANEL_TEMPLATE: '<div class="' + _classNames.tabPanel + '"></div>',
212     _uiSetSelectedPanel: function(selected) {
213         this.get('panelNode').toggleClass(_classNames.selectedPanel, selected);
214     },
216     _afterTabSelectedChange: function(event) {
217        this._uiSetSelectedPanel(event.newVal);
218     },
220     _afterParentChange: function(e) {
221         if (!e.newVal) {
222             this._remove();
223         } else {
224             this._add();
225         }
226     },
228     _initAria: function() {
229         var anchor = this.get('contentBox'),
230             id = anchor.get('id'),
231             panel = this.get('panelNode');
233         if (!id) {
234             id = Y.guid();
235             anchor.set('id', id);
236         }
237         //  Apply the ARIA roles, states and properties to each tab
238         anchor.set('role', 'tab');
239         anchor.get('parentNode').set('role', 'presentation');
242         //  Apply the ARIA roles, states and properties to each panel
243         panel.setAttrs({
244             role: 'tabpanel',
245             'aria-labelledby': id
246         });
247     },
249     syncUI: function() {
250         this.set('label', this.get('label'));
251         this.set('content', this.get('content'));
252         this._uiSetSelectedPanel(this.get('selected'));
253     },
255     bindUI: function() {
256        this.after('selectedChange', this._afterTabSelectedChange);
257        this.after('parentChange', this._afterParentChange);
258     },
260     renderUI: function() {
261         this._renderPanel();
262         this._initAria();
263     },
265     _renderPanel: function() {
266         this.get('parent').get('panelNode')
267             .appendChild(this.get('panelNode'));
268     },
270     _add: function() {
271         var parent = this.get('parent').get('contentBox'),
272             list = parent.get('listNode'),
273             panel = parent.get('panelNode');
275         if (list) {
276             list.appendChild(this.get('boundingBox'));
277         }
279         if (panel) {
280             panel.appendChild(this.get('panelNode'));
281         }
282     },
283     
284     _remove: function() {
285         this.get('boundingBox').remove();
286         this.get('panelNode').remove();
287     },
289     _onActivate: function(e) {
290          if (e.target === this) {
291              //  Prevent the browser from navigating to the URL specified by the 
292              //  anchor's href attribute.
293              e.domEvent.preventDefault();
294              e.target.set('selected', 1);
295          }
296     },
297     
298     initializer: function() {
299        this.publish(this.get('triggerEvent'), { 
300            defaultFn: this._onActivate
301        });
302     },
304     _defLabelSetter: function(label) {
305         this.get('contentBox').setContent(label);
306         return label;
307     },
309     _defContentSetter: function(content) {
310         this.get('panelNode').setContent(content);
311         return content;
312     },
314     _defContentGetter: function(content) {
315         return this.get('panelNode').getContent();
316     },
318     // find panel by ID mapping from label href
319     _defPanelNodeValueFn: function() {
320         var href = this.get('contentBox').get('href') || '',
321             parent = this.get('parent'),
322             hashIndex = href.indexOf('#'),
323             panel;
325         href = href.substr(hashIndex);
327         if (href.charAt(0) === '#') { // in-page nav, find by ID
328             panel = Y.one(href);
329             if (panel) {
330                 panel.addClass(_classNames.tabPanel);
331             }
332         }
334         // use the one found by id, or else try matching indices
335         if (!panel && parent) {
336             panel = parent.get('panelNode')
337                     .get('children').item(this.get('index'));
338         }
340         if (!panel) { // create if none found
341             panel = Y.Node.create(this.PANEL_TEMPLATE);
342         }
343         return panel;
344     }
345 }, {
346     ATTRS: {
347         /**
348          * @attribute triggerEvent
349          * @default "click" 
350          * @type String
351          */
352         triggerEvent: {
353             value: 'click'
354         },
356         /**
357          * @attribute label
358          * @type HTML
359          */
360         label: { 
361             setter: '_defLabelSetter',
362             validator: Lang.isString
363         },
365         /**
366          * @attribute content
367          * @type HTML
368          */
369         content: {
370             setter: '_defContentSetter',
371             getter: '_defContentGetter'
372         },
374         /**
375          * @attribute panelNode
376          * @type Y.Node
377          */
378         panelNode: {
379             setter: function(node) {
380                 node = Y.one(node);
381                 if (node) {
382                     node.addClass(_classNames.tabPanel);
383                 }
384                 return node;
385             },
386             valueFn: '_defPanelNodeValueFn'
387         },
388         
389         tabIndex: {
390             value: null,
391             validator: '_validTabIndex'
392         }        
394     },
396     HTML_PARSER: {
397         selected: function(contentBox) {
398             var ret = (this.get('boundingBox').hasClass(_classNames.selectedTab)) ?
399                         1 : 0;
400             return ret;
401         }
402     }
407 }, '3.5.1' ,{requires:['node-pluginhost', 'node-focusmanager', 'tabview-base', 'widget', 'widget-parent', 'widget-child']});