NOBUG: Fixed file access permissions
[moodle.git] / lib / yuilib / 3.13.0 / tabview / tabview.js
blobb75d7dfdce2324a3f3d237e00d9cf40539a8197d
1 /*
2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
8 YUI.add('tabview', function (Y, NAME) {
10 /**
11  * The TabView module
12  *
13  * @module tabview
14  */
16 var DOT = '.',
18     /**
19      * Provides a tabbed widget interface
20      * @param config {Object} Object literal specifying tabview configuration properties.
21      *
22      * @class TabView
23      * @constructor
24      * @extends Widget
25      * @uses WidgetParent
26      */
27     TabView = Y.Base.create('tabView', Y.Widget, [Y.WidgetParent], {
29     _afterChildAdded: function() {
30         this.get('contentBox').focusManager.refresh();
31     },
33     _defListNodeValueFn: function() {
34         var node = Y.Node.create(this.LIST_TEMPLATE);
36         node.addClass(Y.TabviewBase._classNames.tabviewList);
38         return node;
39     },
41     _defPanelNodeValueFn: function() {
42         var node = Y.Node.create(this.PANEL_TEMPLATE);
44         node.addClass(Y.TabviewBase._classNames.tabviewPanel);
46         return node;
47     },
49     _afterChildRemoved: function(e) { // update the selected tab when removed
50         var i = e.index,
51             selection = this.get('selection');
53         if (!selection) { // select previous item if selection removed
54             selection = this.item(i - 1) || this.item(0);
55             if (selection) {
56                 selection.set('selected', 1);
57             }
58         }
60         this.get('contentBox').focusManager.refresh();
61     },
63     _initAria: function(contentBox) {
64         var tablist = contentBox.one(Y.TabviewBase._queries.tabviewList);
66         if (tablist) {
67             tablist.setAttrs({
68                 //'aria-labelledby':
69                 role: 'tablist'
70             });
71         }
72     },
74     bindUI: function() {
75         //  Use the Node Focus Manager to add keyboard support:
76         //  Pressing the left and right arrow keys will move focus
77         //  among each of the tabs.
79         this.get('contentBox').plug(Y.Plugin.NodeFocusManager, {
80                         descendants: DOT + Y.TabviewBase._classNames.tabLabel,
81                         keys: { next: 'down:39', // Right arrow
82                                 previous: 'down:37' },  // Left arrow
83                         circular: true
84                     });
86         this.after('render', this._setDefSelection);
87         this.after('addChild', this._afterChildAdded);
88         this.after('removeChild', this._afterChildRemoved);
89     },
91     renderUI: function() {
92         var contentBox = this.get('contentBox');
93         this._renderListBox(contentBox);
94         this._renderPanelBox(contentBox);
95         this._childrenContainer = this.get('listNode');
96         this._renderTabs(contentBox);
97         this._initAria(contentBox);
98     },
100     _setDefSelection: function() {
101         //  If no tab is selected, select the first tab.
102         var selection = this.get('selection') || this.item(0);
104         this.some(function(tab) {
105             if (tab.get('selected')) {
106                 selection = tab;
107                 return true;
108             }
109         });
110         if (selection) {
111             // TODO: why both needed? (via widgetParent/Child)?
112             this.set('selection', selection);
113             selection.set('selected', 1);
114         }
115     },
117     _renderListBox: function(contentBox) {
118         var node = this.get('listNode');
119         if (!node.inDoc()) {
120             contentBox.append(node);
121         }
122     },
124     _renderPanelBox: function(contentBox) {
125         var node = this.get('panelNode');
126         if (!node.inDoc()) {
127             contentBox.append(node);
128         }
129     },
131     _renderTabs: function(contentBox) {
132         var _classNames = Y.TabviewBase._classNames,
133             _queries = Y.TabviewBase._queries,
134             tabs = contentBox.all(_queries.tab),
135             panelNode = this.get('panelNode'),
136             panels = (panelNode) ? this.get('panelNode').get('children') : null,
137             tabview = this;
139         if (tabs) { // add classNames and fill in Tab fields from markup when possible
140             tabs.addClass(_classNames.tab);
141             contentBox.all(_queries.tabLabel).addClass(_classNames.tabLabel);
142             contentBox.all(_queries.tabPanel).addClass(_classNames.tabPanel);
144             tabs.each(function(node, i) {
145                 var panelNode = (panels) ? panels.item(i) : null;
146                 tabview.add({
147                     boundingBox: node,
148                     contentBox: node.one(DOT + _classNames.tabLabel),
149                     panelNode: panelNode
150                 });
151             });
152         }
153     }
154 }, {
155     ATTRS: {
156         defaultChildType: {
157             value: 'Tab'
158         },
160         listNode: {
161             setter: function(node) {
162                 node = Y.one(node);
163                 if (node) {
164                     node.addClass(Y.TabviewBase._classNames.tabviewList);
165                 }
166                 return node;
167             },
169             valueFn: '_defListNodeValueFn'
170         },
172         panelNode: {
173             setter: function(node) {
174                 node = Y.one(node);
175                 if (node) {
176                     node.addClass(Y.TabviewBase._classNames.tabviewPanel);
177                 }
178                 return node;
179             },
181             valueFn: '_defPanelNodeValueFn'
182         },
184         tabIndex: {
185             value: null
186             //validator: '_validTabIndex'
187         }
188     },
190     HTML_PARSER: {
191         listNode: function(srcNode) {
192             return srcNode.one(Y.TabviewBase._queries.tabviewList);
193         },
194         panelNode: function(srcNode) {
195             return srcNode.one(Y.TabviewBase._queries.tabviewPanel);
196         }
197     },
199     // Static for legacy support.
200     LIST_TEMPLATE: '<ul></ul>',
201     PANEL_TEMPLATE: '<div></div>'
204 // Map to static values by default.
205 TabView.prototype.LIST_TEMPLATE = TabView.LIST_TEMPLATE;
206 TabView.prototype.PANEL_TEMPLATE = TabView.PANEL_TEMPLATE;
208 Y.TabView = TabView;
210  * Provides Tab instances for use with TabView
211  * @param config {Object} Object literal specifying tabview configuration properties.
213  * @class Tab
214  * @constructor
215  * @extends Widget
216  * @uses WidgetChild
217  */
218 Y.Tab = Y.Base.create('tab', Y.Widget, [Y.WidgetChild], {
219     BOUNDING_TEMPLATE: '<li></li>',
220     CONTENT_TEMPLATE: '<a></a>',
221     PANEL_TEMPLATE: '<div></div>',
223     _uiSetSelectedPanel: function(selected) {
224         this.get('panelNode').toggleClass(Y.TabviewBase._classNames.selectedPanel, selected);
225     },
227     _afterTabSelectedChange: function(event) {
228        this._uiSetSelectedPanel(event.newVal);
229     },
231     _afterParentChange: function(e) {
232         if (!e.newVal) {
233             this._remove();
234         } else {
235             this._add();
236         }
237     },
239     _initAria: function() {
240         var anchor = this.get('contentBox'),
241             id = anchor.get('id'),
242             panel = this.get('panelNode');
244         if (!id) {
245             id = Y.guid();
246             anchor.set('id', id);
247         }
248         //  Apply the ARIA roles, states and properties to each tab
249         anchor.set('role', 'tab');
250         anchor.get('parentNode').set('role', 'presentation');
252         //  Apply the ARIA roles, states and properties to each panel
253         panel.setAttrs({
254             role: 'tabpanel',
255             'aria-labelledby': id
256         });
257     },
259     syncUI: function() {
260         var _classNames = Y.TabviewBase._classNames;
262         this.get('boundingBox').addClass(_classNames.tab);
263         this.get('contentBox').addClass(_classNames.tabLabel);
264         this.set('label', this.get('label'));
265         this.set('content', this.get('content'));
266         this._uiSetSelectedPanel(this.get('selected'));
267     },
269     bindUI: function() {
270        this.after('selectedChange', this._afterTabSelectedChange);
271        this.after('parentChange', this._afterParentChange);
272     },
274     renderUI: function() {
275         this._renderPanel();
276         this._initAria();
277     },
279     _renderPanel: function() {
280         this.get('parent').get('panelNode')
281             .appendChild(this.get('panelNode'));
282     },
284     _add: function() {
285         var parent = this.get('parent').get('contentBox'),
286             list = parent.get('listNode'),
287             panel = parent.get('panelNode');
289         if (list) {
290             list.appendChild(this.get('boundingBox'));
291         }
293         if (panel) {
294             panel.appendChild(this.get('panelNode'));
295         }
296     },
298     _remove: function() {
299         this.get('boundingBox').remove();
300         this.get('panelNode').remove();
301     },
303     _onActivate: function(e) {
304          if (e.target === this) {
305              //  Prevent the browser from navigating to the URL specified by the
306              //  anchor's href attribute.
307              e.domEvent.preventDefault();
308              e.target.set('selected', 1);
309          }
310     },
312     initializer: function() {
313        this.publish(this.get('triggerEvent'), {
314            defaultFn: this._onActivate
315        });
316     },
318     _defLabelGetter: function() {
319         return this.get('contentBox').getHTML();
320     },
322     _defLabelSetter: function(label) {
323         var labelNode = this.get('contentBox');
324         if (labelNode.getHTML() !== label) { // Avoid rewriting existing label.
325             labelNode.setHTML(label);
326         }
327         return label;
328     },
330     _defContentSetter: function(content) {
331         var panel = this.get('panelNode');
332         if (panel.getHTML() !== content) { // Avoid rewriting existing content.
333             panel.setHTML(content);
334         }
335         return content;
336     },
338     _defContentGetter: function() {
339         return this.get('panelNode').getHTML();
340     },
342     // find panel by ID mapping from label href
343     _defPanelNodeValueFn: function() {
344         var _classNames = Y.TabviewBase._classNames,
345             href = this.get('contentBox').get('href') || '',
346             parent = this.get('parent'),
347             hashIndex = href.indexOf('#'),
348             panel;
350         href = href.substr(hashIndex);
352         if (href.charAt(0) === '#') { // in-page nav, find by ID
353             panel = Y.one(href);
354             if (panel) {
355                 panel.addClass(_classNames.tabPanel);
356             }
357         }
359         // use the one found by id, or else try matching indices
360         if (!panel && parent) {
361             panel = parent.get('panelNode')
362                     .get('children').item(this.get('index'));
363         }
365         if (!panel) { // create if none found
366             panel = Y.Node.create(this.PANEL_TEMPLATE);
367             panel.addClass(_classNames.tabPanel);
368         }
369         return panel;
370     }
371 }, {
372     ATTRS: {
373         /**
374          * @attribute triggerEvent
375          * @default "click"
376          * @type String
377          */
378         triggerEvent: {
379             value: 'click'
380         },
382         /**
383          * @attribute label
384          * @type HTML
385          */
386         label: {
387             setter: '_defLabelSetter',
388             getter: '_defLabelGetter'
389         },
391         /**
392          * @attribute content
393          * @type HTML
394          */
395         content: {
396             setter: '_defContentSetter',
397             getter: '_defContentGetter'
398         },
400         /**
401          * @attribute panelNode
402          * @type Y.Node
403          */
404         panelNode: {
405             setter: function(node) {
406                 node = Y.one(node);
407                 if (node) {
408                     node.addClass(Y.TabviewBase._classNames.tabPanel);
409                 }
410                 return node;
411             },
412             valueFn: '_defPanelNodeValueFn'
413         },
415         tabIndex: {
416             value: null,
417             validator: '_validTabIndex'
418         }
420     },
422     HTML_PARSER: {
423         selected: function() {
424             var ret = (this.get('boundingBox').hasClass(Y.TabviewBase._classNames.selectedTab)) ?
425                         1 : 0;
426             return ret;
427         }
428     }
433 }, '3.13.0', {
434     "requires": [
435         "widget",
436         "widget-parent",
437         "widget-child",
438         "tabview-base",
439         "node-pluginhost",
440         "node-focusmanager"
441     ],
442     "skinnable": true