3
Copyright 2011 Yahoo! Inc. All rights reserved.
4
Licensed under the BSD License.
5
http://yuilibrary.com/license/
7
YUI.add('tabview', function(Y) {
15
var _queries = Y.TabviewBase._queries,
16
_classNames = Y.TabviewBase._classNames,
18
getClassName = Y.ClassNameManager.getClassName,
21
* Provides a tabbed widget interface
22
* @param config {Object} Object literal specifying tabview configuration properties.
29
TabView = Y.Base.create('tabView', Y.Widget, [Y.WidgetParent], {
30
_afterChildAdded: function(e) {
31
this.get('contentBox').focusManager.refresh();
34
_defListNodeValueFn: function() {
35
return Y.Node.create(TabView.LIST_TEMPLATE);
38
_defPanelNodeValueFn: function() {
39
return Y.Node.create(TabView.PANEL_TEMPLATE);
42
_afterChildRemoved: function(e) { // update the selected tab when removed
44
selection = this.get('selection');
46
if (!selection) { // select previous item if selection removed
47
selection = this.item(i - 1) || this.item(0);
49
selection.set('selected', 1);
53
this.get('contentBox').focusManager.refresh();
56
_initAria: function() {
57
var contentBox = this.get('contentBox'),
58
tablist = contentBox.one(_queries.tabviewList);
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
80
this.after('render', this._setDefSelection);
81
this.after('addChild', this._afterChildAdded);
82
this.after('removeChild', this._afterChildRemoved);
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);
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')) {
104
// TODO: why both needed? (via widgetParent/Child)?
105
this.set('selection', selection);
106
selection.set('selected', 1);
110
_renderListBox: function(contentBox) {
111
var node = this.get('listNode');
113
contentBox.append(node);
117
_renderPanelBox: function(contentBox) {
118
var node = this.get('panelNode');
120
contentBox.append(node);
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,
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;
139
contentBox: node.one(DOT + _classNames.tabLabel),
140
label: node.one(DOT + _classNames.tabLabel).get('text'),
148
LIST_TEMPLATE: '<ul class="' + _classNames.tabviewList + '"></ul>',
149
PANEL_TEMPLATE: '<div class="' + _classNames.tabviewPanel + '"></div>',
157
setter: function(node) {
160
node.addClass(_classNames.tabviewList);
165
valueFn: '_defListNodeValueFn'
169
setter: function(node) {
172
node.addClass(_classNames.tabviewPanel);
177
valueFn: '_defPanelNodeValueFn'
182
//validator: '_validTabIndex'
187
listNode: _queries.tabviewList,
188
panelNode: _queries.tabviewPanel
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.
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);
216
_afterTabSelectedChange: function(event) {
217
this._uiSetSelectedPanel(event.newVal);
220
_afterParentChange: function(e) {
228
_initAria: function() {
229
var anchor = this.get('contentBox'),
230
id = anchor.get('id'),
231
panel = this.get('panelNode');
235
anchor.set('id', id);
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
245
'aria-labelledby': id
250
this.set('label', this.get('label'));
251
this.set('content', this.get('content'));
252
this._uiSetSelectedPanel(this.get('selected'));
256
this.after('selectedChange', this._afterTabSelectedChange);
257
this.after('parentChange', this._afterParentChange);
260
renderUI: function() {
265
_renderPanel: function() {
266
this.get('parent').get('panelNode')
267
.appendChild(this.get('panelNode'));
271
var parent = this.get('parent').get('contentBox'),
272
list = parent.get('listNode'),
273
panel = parent.get('panelNode');
276
list.appendChild(this.get('boundingBox'));
280
panel.appendChild(this.get('panelNode'));
284
_remove: function() {
285
this.get('boundingBox').remove();
286
this.get('panelNode').remove();
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);
298
initializer: function() {
299
this.publish(this.get('triggerEvent'), {
300
defaultFn: this._onActivate
304
_defLabelSetter: function(label) {
305
this.get('contentBox').setContent(label);
309
_defContentSetter: function(content) {
310
this.get('panelNode').setContent(content);
314
_defContentGetter: function(content) {
315
return this.get('panelNode').getContent();
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('#'),
325
href = href.substr(hashIndex);
327
if (href.charAt(0) === '#') { // in-page nav, find by ID
330
panel.addClass(_classNames.tabPanel);
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'));
340
if (!panel) { // create if none found
341
panel = Y.Node.create(this.PANEL_TEMPLATE);
348
* @attribute triggerEvent
361
setter: '_defLabelSetter',
362
validator: Lang.isString
370
setter: '_defContentSetter',
371
getter: '_defContentGetter'
375
* @attribute panelNode
379
setter: function(node) {
382
node.addClass(_classNames.tabPanel);
386
valueFn: '_defPanelNodeValueFn'
391
validator: '_validTabIndex'
397
selected: function(contentBox) {
398
var ret = (this.get('boundingBox').hasClass(_classNames.selectedTab)) ?
407
}, '3.4.1' ,{requires:['node-pluginhost', 'node-focusmanager', 'tabview-base', 'widget', 'widget-parent', 'widget-child']});