0
|
1 /*
|
|
2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
|
|
3 Code licensed under the BSD License:
|
|
4 http://developer.yahoo.net/yui/license.txt
|
|
5 version: 2.8.0r4
|
|
6 */
|
|
7 (function() {
|
|
8
|
|
9 /**
|
|
10 * The tabview module provides a widget for managing content bound to tabs.
|
|
11 * @module tabview
|
|
12 * @requires yahoo, dom, event, element
|
|
13 *
|
|
14 */
|
|
15
|
|
16 var Y = YAHOO.util,
|
|
17 Dom = Y.Dom,
|
|
18 Event = Y.Event,
|
|
19 document = window.document,
|
|
20
|
|
21 // STRING CONSTANTS
|
|
22 ACTIVE = 'active',
|
|
23 ACTIVE_INDEX = 'activeIndex',
|
|
24 ACTIVE_TAB = 'activeTab',
|
|
25 CONTENT_EL = 'contentEl',
|
|
26 ELEMENT = 'element',
|
|
27
|
|
28 /**
|
|
29 * A widget to control tabbed views.
|
|
30 * @namespace YAHOO.widget
|
|
31 * @class TabView
|
|
32 * @extends YAHOO.util.Element
|
|
33 * @constructor
|
|
34 * @param {HTMLElement | String | Object} el(optional) The html
|
|
35 * element that represents the TabView, or the attribute object to use.
|
|
36 * An element will be created if none provided.
|
|
37 * @param {Object} attr (optional) A key map of the tabView's
|
|
38 * initial attributes. Ignored if first arg is attributes object.
|
|
39 */
|
|
40 TabView = function(el, attr) {
|
|
41 attr = attr || {};
|
|
42 if (arguments.length == 1 && !YAHOO.lang.isString(el) && !el.nodeName) {
|
|
43 attr = el; // treat first arg as attr object
|
|
44 el = attr.element || null;
|
|
45 }
|
|
46
|
|
47 if (!el && !attr.element) { // create if we dont have one
|
|
48 el = this._createTabViewElement(attr);
|
|
49 }
|
|
50 TabView.superclass.constructor.call(this, el, attr);
|
|
51 };
|
|
52
|
|
53 YAHOO.extend(TabView, Y.Element, {
|
|
54 /**
|
|
55 * The className to add when building from scratch.
|
|
56 * @property CLASSNAME
|
|
57 * @default "navset"
|
|
58 */
|
|
59 CLASSNAME: 'yui-navset',
|
|
60
|
|
61 /**
|
|
62 * The className of the HTMLElement containing the TabView's tab elements
|
|
63 * to look for when building from existing markup, or to add when building
|
|
64 * from scratch.
|
|
65 * All childNodes of the tab container are treated as Tabs when building
|
|
66 * from existing markup.
|
|
67 * @property TAB_PARENT_CLASSNAME
|
|
68 * @default "nav"
|
|
69 */
|
|
70 TAB_PARENT_CLASSNAME: 'yui-nav',
|
|
71
|
|
72 /**
|
|
73 * The className of the HTMLElement containing the TabView's label elements
|
|
74 * to look for when building from existing markup, or to add when building
|
|
75 * from scratch.
|
|
76 * All childNodes of the content container are treated as content elements when
|
|
77 * building from existing markup.
|
|
78 * @property CONTENT_PARENT_CLASSNAME
|
|
79 * @default "nav-content"
|
|
80 */
|
|
81 CONTENT_PARENT_CLASSNAME: 'yui-content',
|
|
82
|
|
83 _tabParent: null,
|
|
84 _contentParent: null,
|
|
85
|
|
86 /**
|
|
87 * Adds a Tab to the TabView instance.
|
|
88 * If no index is specified, the tab is added to the end of the tab list.
|
|
89 * @method addTab
|
|
90 * @param {YAHOO.widget.Tab} tab A Tab instance to add.
|
|
91 * @param {Integer} index The position to add the tab.
|
|
92 * @return void
|
|
93 */
|
|
94 addTab: function(tab, index) {
|
|
95 var tabs = this.get('tabs'),
|
|
96 before = this.getTab(index),
|
|
97 tabParent = this._tabParent,
|
|
98 contentParent = this._contentParent,
|
|
99 tabElement = tab.get(ELEMENT),
|
|
100 contentEl = tab.get(CONTENT_EL);
|
|
101
|
|
102 if (!tabs) { // not ready yet
|
|
103 this._queue[this._queue.length] = ['addTab', arguments];
|
|
104 return false;
|
|
105 }
|
|
106
|
|
107 index = (index === undefined) ? tabs.length : index;
|
|
108
|
|
109 tabs.splice(index, 0, tab);
|
|
110
|
|
111 if ( before ) {
|
|
112 tabParent.insertBefore(tabElement, before.get(ELEMENT));
|
|
113 } else {
|
|
114 tabParent.appendChild(tabElement);
|
|
115 }
|
|
116
|
|
117 if ( contentEl && !Dom.isAncestor(contentParent, contentEl) ) {
|
|
118 contentParent.appendChild(contentEl);
|
|
119 }
|
|
120
|
|
121 if ( !tab.get(ACTIVE) ) {
|
|
122 tab.set('contentVisible', false, true); /* hide if not active */
|
|
123 } else {
|
|
124 this.set(ACTIVE_TAB, tab, true);
|
|
125 this.set('activeIndex', index, true);
|
|
126 }
|
|
127
|
|
128 this._initTabEvents(tab);
|
|
129 },
|
|
130
|
|
131 _initTabEvents: function(tab) {
|
|
132 tab.addListener( tab.get('activationEvent'), tab._onActivate, this, tab);
|
|
133 tab.addListener( tab.get('activationEventChange'), tab._onActivationEventChange, this, tab);
|
|
134 },
|
|
135
|
|
136 _removeTabEvents: function(tab) {
|
|
137 tab.removeListener(tab.get('activationEvent'), tab._onActivate, this, tab);
|
|
138 tab.removeListener('activationEventChange', tab._onActivationEventChange, this, tab);
|
|
139 },
|
|
140
|
|
141 /**
|
|
142 * Routes childNode events.
|
|
143 * @method DOMEventHandler
|
|
144 * @param {event} e The Dom event that is being handled.
|
|
145 * @return void
|
|
146 */
|
|
147 DOMEventHandler: function(e) {
|
|
148 var target = Event.getTarget(e),
|
|
149 tabParent = this._tabParent,
|
|
150 tabs = this.get('tabs'),
|
|
151 tab,
|
|
152 tabEl,
|
|
153 contentEl;
|
|
154
|
|
155
|
|
156 if (Dom.isAncestor(tabParent, target) ) {
|
|
157 for (var i = 0, len = tabs.length; i < len; i++) {
|
|
158 tabEl = tabs[i].get(ELEMENT);
|
|
159 contentEl = tabs[i].get(CONTENT_EL);
|
|
160
|
|
161 if ( target == tabEl || Dom.isAncestor(tabEl, target) ) {
|
|
162 tab = tabs[i];
|
|
163 break; // note break
|
|
164 }
|
|
165 }
|
|
166
|
|
167 if (tab) {
|
|
168 tab.fireEvent(e.type, e);
|
|
169 }
|
|
170 }
|
|
171 },
|
|
172
|
|
173 /**
|
|
174 * Returns the Tab instance at the specified index.
|
|
175 * @method getTab
|
|
176 * @param {Integer} index The position of the Tab.
|
|
177 * @return YAHOO.widget.Tab
|
|
178 */
|
|
179 getTab: function(index) {
|
|
180 return this.get('tabs')[index];
|
|
181 },
|
|
182
|
|
183 /**
|
|
184 * Returns the index of given tab.
|
|
185 * @method getTabIndex
|
|
186 * @param {YAHOO.widget.Tab} tab The tab whose index will be returned.
|
|
187 * @return int
|
|
188 */
|
|
189 getTabIndex: function(tab) {
|
|
190 var index = null,
|
|
191 tabs = this.get('tabs');
|
|
192 for (var i = 0, len = tabs.length; i < len; ++i) {
|
|
193 if (tab == tabs[i]) {
|
|
194 index = i;
|
|
195 break;
|
|
196 }
|
|
197 }
|
|
198
|
|
199 return index;
|
|
200 },
|
|
201
|
|
202 /**
|
|
203 * Removes the specified Tab from the TabView.
|
|
204 * @method removeTab
|
|
205 * @param {YAHOO.widget.Tab} item The Tab instance to be removed.
|
|
206 * @return void
|
|
207 */
|
|
208 removeTab: function(tab) {
|
|
209 var tabCount = this.get('tabs').length,
|
|
210 index = this.getTabIndex(tab);
|
|
211
|
|
212 if ( tab === this.get(ACTIVE_TAB) ) {
|
|
213 if (tabCount > 1) { // select another tab
|
|
214 if (index + 1 === tabCount) { // if last, activate previous
|
|
215 this.set(ACTIVE_INDEX, index - 1);
|
|
216 } else { // activate next tab
|
|
217 this.set(ACTIVE_INDEX, index + 1);
|
|
218 }
|
|
219 } else { // no more tabs
|
|
220 this.set(ACTIVE_TAB, null);
|
|
221 }
|
|
222 }
|
|
223
|
|
224 this._removeTabEvents(tab);
|
|
225 this._tabParent.removeChild( tab.get(ELEMENT) );
|
|
226 this._contentParent.removeChild( tab.get(CONTENT_EL) );
|
|
227 this._configs.tabs.value.splice(index, 1);
|
|
228
|
|
229 tab.fireEvent('remove', { type: 'remove', tabview: this });
|
|
230 },
|
|
231
|
|
232 /**
|
|
233 * Provides a readable name for the TabView instance.
|
|
234 * @method toString
|
|
235 * @return String
|
|
236 */
|
|
237 toString: function() {
|
|
238 var name = this.get('id') || this.get('tagName');
|
|
239 return "TabView " + name;
|
|
240 },
|
|
241
|
|
242 /**
|
|
243 * The transiton to use when switching between tabs.
|
|
244 * @method contentTransition
|
|
245 */
|
|
246 contentTransition: function(newTab, oldTab) {
|
|
247 if (newTab) {
|
|
248 newTab.set('contentVisible', true);
|
|
249 }
|
|
250 if (oldTab) {
|
|
251 oldTab.set('contentVisible', false);
|
|
252 }
|
|
253 },
|
|
254
|
|
255 /**
|
|
256 * setAttributeConfigs TabView specific properties.
|
|
257 * @method initAttributes
|
|
258 * @param {Object} attr Hash of initial attributes
|
|
259 */
|
|
260 initAttributes: function(attr) {
|
|
261 TabView.superclass.initAttributes.call(this, attr);
|
|
262
|
|
263 if (!attr.orientation) {
|
|
264 attr.orientation = 'top';
|
|
265 }
|
|
266
|
|
267 var el = this.get(ELEMENT);
|
|
268
|
|
269 if (!Dom.hasClass(el, this.CLASSNAME)) {
|
|
270 Dom.addClass(el, this.CLASSNAME);
|
|
271 }
|
|
272
|
|
273 /**
|
|
274 * The Tabs belonging to the TabView instance.
|
|
275 * @attribute tabs
|
|
276 * @type Array
|
|
277 */
|
|
278 this.setAttributeConfig('tabs', {
|
|
279 value: [],
|
|
280 readOnly: true
|
|
281 });
|
|
282
|
|
283 /**
|
|
284 * The container of the tabView's label elements.
|
|
285 * @property _tabParent
|
|
286 * @private
|
|
287 * @type HTMLElement
|
|
288 */
|
|
289 this._tabParent =
|
|
290 this.getElementsByClassName(this.TAB_PARENT_CLASSNAME,
|
|
291 'ul' )[0] || this._createTabParent();
|
|
292
|
|
293 /**
|
|
294 * The container of the tabView's content elements.
|
|
295 * @property _contentParent
|
|
296 * @type HTMLElement
|
|
297 * @private
|
|
298 */
|
|
299 this._contentParent =
|
|
300 this.getElementsByClassName(this.CONTENT_PARENT_CLASSNAME,
|
|
301 'div')[0] || this._createContentParent();
|
|
302
|
|
303 /**
|
|
304 * How the Tabs should be oriented relative to the TabView.
|
|
305 * @attribute orientation
|
|
306 * @type String
|
|
307 * @default "top"
|
|
308 */
|
|
309 this.setAttributeConfig('orientation', {
|
|
310 value: attr.orientation,
|
|
311 method: function(value) {
|
|
312 var current = this.get('orientation');
|
|
313 this.addClass('yui-navset-' + value);
|
|
314
|
|
315 if (current != value) {
|
|
316 this.removeClass('yui-navset-' + current);
|
|
317 }
|
|
318
|
|
319 if (value === 'bottom') {
|
|
320 this.appendChild(this._tabParent);
|
|
321 }
|
|
322 }
|
|
323 });
|
|
324
|
|
325 /**
|
|
326 * The index of the tab currently active.
|
|
327 * @attribute activeIndex
|
|
328 * @type Int
|
|
329 */
|
|
330 this.setAttributeConfig(ACTIVE_INDEX, {
|
|
331 value: attr.activeIndex,
|
|
332 validator: function(value) {
|
|
333 var ret = true;
|
|
334 if (value && this.getTab(value).get('disabled')) { // cannot activate if disabled
|
|
335 ret = false;
|
|
336 }
|
|
337 return ret;
|
|
338 }
|
|
339 });
|
|
340
|
|
341 /**
|
|
342 * The tab currently active.
|
|
343 * @attribute activeTab
|
|
344 * @type YAHOO.widget.Tab
|
|
345 */
|
|
346 this.setAttributeConfig(ACTIVE_TAB, {
|
|
347 value: attr.activeTab,
|
|
348 method: function(tab) {
|
|
349 var activeTab = this.get(ACTIVE_TAB);
|
|
350
|
|
351 if (tab) {
|
|
352 tab.set(ACTIVE, true);
|
|
353 }
|
|
354
|
|
355 if (activeTab && activeTab !== tab) {
|
|
356 activeTab.set(ACTIVE, false);
|
|
357 }
|
|
358
|
|
359 if (activeTab && tab !== activeTab) { // no transition if only 1
|
|
360 this.contentTransition(tab, activeTab);
|
|
361 } else if (tab) {
|
|
362 tab.set('contentVisible', true);
|
|
363 }
|
|
364 },
|
|
365 validator: function(value) {
|
|
366 var ret = true;
|
|
367 if (value && value.get('disabled')) { // cannot activate if disabled
|
|
368 ret = false;
|
|
369 }
|
|
370 return ret;
|
|
371 }
|
|
372 });
|
|
373
|
|
374 this.on('activeTabChange', this._onActiveTabChange);
|
|
375 this.on('activeIndexChange', this._onActiveIndexChange);
|
|
376
|
|
377 if ( this._tabParent ) {
|
|
378 this._initTabs();
|
|
379 }
|
|
380
|
|
381 // Due to delegation we add all DOM_EVENTS to the TabView container
|
|
382 // but IE will leak when unsupported events are added, so remove these
|
|
383 this.DOM_EVENTS.submit = false;
|
|
384 this.DOM_EVENTS.focus = false;
|
|
385 this.DOM_EVENTS.blur = false;
|
|
386
|
|
387 for (var type in this.DOM_EVENTS) {
|
|
388 if ( YAHOO.lang.hasOwnProperty(this.DOM_EVENTS, type) ) {
|
|
389 this.addListener.call(this, type, this.DOMEventHandler);
|
|
390 }
|
|
391 }
|
|
392 },
|
|
393
|
|
394 /**
|
|
395 * Removes selected state from the given tab if it is the activeTab
|
|
396 * @method deselectTab
|
|
397 * @param {Int} index The tab index to deselect
|
|
398 */
|
|
399 deselectTab: function(index) {
|
|
400 if (this.getTab(index) === this.get('activeTab')) {
|
|
401 this.set('activeTab', null);
|
|
402 }
|
|
403 },
|
|
404
|
|
405 /**
|
|
406 * Makes the tab at the given index the active tab
|
|
407 * @method selectTab
|
|
408 * @param {Int} index The tab index to be made active
|
|
409 */
|
|
410 selectTab: function(index) {
|
|
411 this.set('activeTab', this.getTab(index));
|
|
412 },
|
|
413
|
|
414 _onActiveTabChange: function(e) {
|
|
415 var activeIndex = this.get(ACTIVE_INDEX),
|
|
416 newIndex = this.getTabIndex(e.newValue);
|
|
417
|
|
418 if (activeIndex !== newIndex) {
|
|
419 if (!(this.set(ACTIVE_INDEX, newIndex)) ) { // NOTE: setting
|
|
420 // revert if activeIndex update fails (cancelled via beforeChange)
|
|
421 this.set(ACTIVE_TAB, e.prevValue);
|
|
422 }
|
|
423 }
|
|
424 },
|
|
425
|
|
426 _onActiveIndexChange: function(e) {
|
|
427 // no set if called from ActiveTabChange event
|
|
428 if (e.newValue !== this.getTabIndex(this.get(ACTIVE_TAB))) {
|
|
429 if (!(this.set(ACTIVE_TAB, this.getTab(e.newValue))) ) { // NOTE: setting
|
|
430 // revert if activeTab update fails (cancelled via beforeChange)
|
|
431 this.set(ACTIVE_INDEX, e.prevValue);
|
|
432 }
|
|
433 }
|
|
434 },
|
|
435
|
|
436 /**
|
|
437 * Creates Tab instances from a collection of HTMLElements.
|
|
438 * @method _initTabs
|
|
439 * @private
|
|
440 * @return void
|
|
441 */
|
|
442 _initTabs: function() {
|
|
443 var tabs = Dom.getChildren(this._tabParent),
|
|
444 contentElements = Dom.getChildren(this._contentParent),
|
|
445 activeIndex = this.get(ACTIVE_INDEX),
|
|
446 tab,
|
|
447 attr,
|
|
448 active;
|
|
449
|
|
450 for (var i = 0, len = tabs.length; i < len; ++i) {
|
|
451 attr = {};
|
|
452
|
|
453 if (contentElements[i]) {
|
|
454 attr.contentEl = contentElements[i];
|
|
455 }
|
|
456
|
|
457 tab = new YAHOO.widget.Tab(tabs[i], attr);
|
|
458 this.addTab(tab);
|
|
459
|
|
460 if (tab.hasClass(tab.ACTIVE_CLASSNAME) ) {
|
|
461 active = tab;
|
|
462 }
|
|
463 }
|
|
464 if (activeIndex) {
|
|
465 this.set(ACTIVE_TAB, this.getTab(activeIndex));
|
|
466 } else {
|
|
467 this._configs.activeTab.value = active; // dont invoke method
|
|
468 this._configs.activeIndex.value = this.getTabIndex(active);
|
|
469 }
|
|
470 },
|
|
471
|
|
472 _createTabViewElement: function(attr) {
|
|
473 var el = document.createElement('div');
|
|
474
|
|
475 if ( this.CLASSNAME ) {
|
|
476 el.className = this.CLASSNAME;
|
|
477 }
|
|
478
|
|
479 return el;
|
|
480 },
|
|
481
|
|
482 _createTabParent: function(attr) {
|
|
483 var el = document.createElement('ul');
|
|
484
|
|
485 if ( this.TAB_PARENT_CLASSNAME ) {
|
|
486 el.className = this.TAB_PARENT_CLASSNAME;
|
|
487 }
|
|
488
|
|
489 this.get(ELEMENT).appendChild(el);
|
|
490
|
|
491 return el;
|
|
492 },
|
|
493
|
|
494 _createContentParent: function(attr) {
|
|
495 var el = document.createElement('div');
|
|
496
|
|
497 if ( this.CONTENT_PARENT_CLASSNAME ) {
|
|
498 el.className = this.CONTENT_PARENT_CLASSNAME;
|
|
499 }
|
|
500
|
|
501 this.get(ELEMENT).appendChild(el);
|
|
502
|
|
503 return el;
|
|
504 }
|
|
505 });
|
|
506
|
|
507
|
|
508 YAHOO.widget.TabView = TabView;
|
|
509 })();
|
|
510
|
|
511 (function() {
|
|
512 var Y = YAHOO.util,
|
|
513 Dom = Y.Dom,
|
|
514 Lang = YAHOO.lang,
|
|
515
|
|
516
|
|
517 // STRING CONSTANTS
|
|
518 ACTIVE_TAB = 'activeTab',
|
|
519 LABEL = 'label',
|
|
520 LABEL_EL = 'labelEl',
|
|
521 CONTENT = 'content',
|
|
522 CONTENT_EL = 'contentEl',
|
|
523 ELEMENT = 'element',
|
|
524 CACHE_DATA = 'cacheData',
|
|
525 DATA_SRC = 'dataSrc',
|
|
526 DATA_LOADED = 'dataLoaded',
|
|
527 DATA_TIMEOUT = 'dataTimeout',
|
|
528 LOAD_METHOD = 'loadMethod',
|
|
529 POST_DATA = 'postData',
|
|
530 DISABLED = 'disabled',
|
|
531
|
|
532 /**
|
|
533 * A representation of a Tab's label and content.
|
|
534 * @namespace YAHOO.widget
|
|
535 * @class Tab
|
|
536 * @extends YAHOO.util.Element
|
|
537 * @constructor
|
|
538 * @param element {HTMLElement | String} (optional) The html element that
|
|
539 * represents the Tab. An element will be created if none provided.
|
|
540 * @param {Object} properties A key map of initial properties
|
|
541 */
|
|
542 Tab = function(el, attr) {
|
|
543 attr = attr || {};
|
|
544 if (arguments.length == 1 && !Lang.isString(el) && !el.nodeName) {
|
|
545 attr = el;
|
|
546 el = attr.element;
|
|
547 }
|
|
548
|
|
549 if (!el && !attr.element) {
|
|
550 el = this._createTabElement(attr);
|
|
551 }
|
|
552
|
|
553 this.loadHandler = {
|
|
554 success: function(o) {
|
|
555 this.set(CONTENT, o.responseText);
|
|
556 },
|
|
557 failure: function(o) {
|
|
558 }
|
|
559 };
|
|
560
|
|
561 Tab.superclass.constructor.call(this, el, attr);
|
|
562
|
|
563 this.DOM_EVENTS = {}; // delegating to tabView
|
|
564 };
|
|
565
|
|
566 YAHOO.extend(Tab, YAHOO.util.Element, {
|
|
567 /**
|
|
568 * The default tag name for a Tab's inner element.
|
|
569 * @property LABEL_INNER_TAGNAME
|
|
570 * @type String
|
|
571 * @default "em"
|
|
572 */
|
|
573 LABEL_TAGNAME: 'em',
|
|
574
|
|
575 /**
|
|
576 * The class name applied to active tabs.
|
|
577 * @property ACTIVE_CLASSNAME
|
|
578 * @type String
|
|
579 * @default "selected"
|
|
580 */
|
|
581 ACTIVE_CLASSNAME: 'selected',
|
|
582
|
|
583 /**
|
|
584 * The class name applied to active tabs.
|
|
585 * @property HIDDEN_CLASSNAME
|
|
586 * @type String
|
|
587 * @default "yui-hidden"
|
|
588 */
|
|
589 HIDDEN_CLASSNAME: 'yui-hidden',
|
|
590
|
|
591 /**
|
|
592 * The title applied to active tabs.
|
|
593 * @property ACTIVE_TITLE
|
|
594 * @type String
|
|
595 * @default "active"
|
|
596 */
|
|
597 ACTIVE_TITLE: 'active',
|
|
598
|
|
599 /**
|
|
600 * The class name applied to disabled tabs.
|
|
601 * @property DISABLED_CLASSNAME
|
|
602 * @type String
|
|
603 * @default "disabled"
|
|
604 */
|
|
605 DISABLED_CLASSNAME: DISABLED,
|
|
606
|
|
607 /**
|
|
608 * The class name applied to dynamic tabs while loading.
|
|
609 * @property LOADING_CLASSNAME
|
|
610 * @type String
|
|
611 * @default "disabled"
|
|
612 */
|
|
613 LOADING_CLASSNAME: 'loading',
|
|
614
|
|
615 /**
|
|
616 * Provides a reference to the connection request object when data is
|
|
617 * loaded dynamically.
|
|
618 * @property dataConnection
|
|
619 * @type Object
|
|
620 */
|
|
621 dataConnection: null,
|
|
622
|
|
623 /**
|
|
624 * Object containing success and failure callbacks for loading data.
|
|
625 * @property loadHandler
|
|
626 * @type object
|
|
627 */
|
|
628 loadHandler: null,
|
|
629
|
|
630 _loading: false,
|
|
631
|
|
632 /**
|
|
633 * Provides a readable name for the tab.
|
|
634 * @method toString
|
|
635 * @return String
|
|
636 */
|
|
637 toString: function() {
|
|
638 var el = this.get(ELEMENT),
|
|
639 id = el.id || el.tagName;
|
|
640 return "Tab " + id;
|
|
641 },
|
|
642
|
|
643 /**
|
|
644 * setAttributeConfigs Tab specific properties.
|
|
645 * @method initAttributes
|
|
646 * @param {Object} attr Hash of initial attributes
|
|
647 */
|
|
648 initAttributes: function(attr) {
|
|
649 attr = attr || {};
|
|
650 Tab.superclass.initAttributes.call(this, attr);
|
|
651
|
|
652 /**
|
|
653 * The event that triggers the tab's activation.
|
|
654 * @attribute activationEvent
|
|
655 * @type String
|
|
656 */
|
|
657 this.setAttributeConfig('activationEvent', {
|
|
658 value: attr.activationEvent || 'click'
|
|
659 });
|
|
660
|
|
661 /**
|
|
662 * The element that contains the tab's label.
|
|
663 * @attribute labelEl
|
|
664 * @type HTMLElement
|
|
665 */
|
|
666 this.setAttributeConfig(LABEL_EL, {
|
|
667 value: attr[LABEL_EL] || this._getLabelEl(),
|
|
668 method: function(value) {
|
|
669 value = Dom.get(value);
|
|
670 var current = this.get(LABEL_EL);
|
|
671
|
|
672 if (current) {
|
|
673 if (current == value) {
|
|
674 return false; // already set
|
|
675 }
|
|
676
|
|
677 current.parentNode.replaceChild(value, current);
|
|
678 this.set(LABEL, value.innerHTML);
|
|
679 }
|
|
680 }
|
|
681 });
|
|
682
|
|
683 /**
|
|
684 * The tab's label text (or innerHTML).
|
|
685 * @attribute label
|
|
686 * @type String
|
|
687 */
|
|
688 this.setAttributeConfig(LABEL, {
|
|
689 value: attr.label || this._getLabel(),
|
|
690 method: function(value) {
|
|
691 var labelEl = this.get(LABEL_EL);
|
|
692 if (!labelEl) { // create if needed
|
|
693 this.set(LABEL_EL, this._createLabelEl());
|
|
694 }
|
|
695
|
|
696 labelEl.innerHTML = value;
|
|
697 }
|
|
698 });
|
|
699
|
|
700 /**
|
|
701 * The HTMLElement that contains the tab's content.
|
|
702 * @attribute contentEl
|
|
703 * @type HTMLElement
|
|
704 */
|
|
705 this.setAttributeConfig(CONTENT_EL, {
|
|
706 value: attr[CONTENT_EL] || document.createElement('div'),
|
|
707 method: function(value) {
|
|
708 value = Dom.get(value);
|
|
709 var current = this.get(CONTENT_EL);
|
|
710
|
|
711 if (current) {
|
|
712 if (current === value) {
|
|
713 return false; // already set
|
|
714 }
|
|
715 if (!this.get('selected')) {
|
|
716 Dom.addClass(value, this.HIDDEN_CLASSNAME);
|
|
717 }
|
|
718 current.parentNode.replaceChild(value, current);
|
|
719 this.set(CONTENT, value.innerHTML);
|
|
720 }
|
|
721 }
|
|
722 });
|
|
723
|
|
724 /**
|
|
725 * The tab's content.
|
|
726 * @attribute content
|
|
727 * @type String
|
|
728 */
|
|
729 this.setAttributeConfig(CONTENT, {
|
|
730 value: attr[CONTENT],
|
|
731 method: function(value) {
|
|
732 this.get(CONTENT_EL).innerHTML = value;
|
|
733 }
|
|
734 });
|
|
735
|
|
736 /**
|
|
737 * The tab's data source, used for loading content dynamically.
|
|
738 * @attribute dataSrc
|
|
739 * @type String
|
|
740 */
|
|
741 this.setAttributeConfig(DATA_SRC, {
|
|
742 value: attr.dataSrc
|
|
743 });
|
|
744
|
|
745 /**
|
|
746 * Whether or not content should be reloaded for every view.
|
|
747 * @attribute cacheData
|
|
748 * @type Boolean
|
|
749 * @default false
|
|
750 */
|
|
751 this.setAttributeConfig(CACHE_DATA, {
|
|
752 value: attr.cacheData || false,
|
|
753 validator: Lang.isBoolean
|
|
754 });
|
|
755
|
|
756 /**
|
|
757 * The method to use for the data request.
|
|
758 * @attribute loadMethod
|
|
759 * @type String
|
|
760 * @default "GET"
|
|
761 */
|
|
762 this.setAttributeConfig(LOAD_METHOD, {
|
|
763 value: attr.loadMethod || 'GET',
|
|
764 validator: Lang.isString
|
|
765 });
|
|
766
|
|
767 /**
|
|
768 * Whether or not any data has been loaded from the server.
|
|
769 * @attribute dataLoaded
|
|
770 * @type Boolean
|
|
771 */
|
|
772 this.setAttributeConfig(DATA_LOADED, {
|
|
773 value: false,
|
|
774 validator: Lang.isBoolean,
|
|
775 writeOnce: true
|
|
776 });
|
|
777
|
|
778 /**
|
|
779 * Number if milliseconds before aborting and calling failure handler.
|
|
780 * @attribute dataTimeout
|
|
781 * @type Number
|
|
782 * @default null
|
|
783 */
|
|
784 this.setAttributeConfig(DATA_TIMEOUT, {
|
|
785 value: attr.dataTimeout || null,
|
|
786 validator: Lang.isNumber
|
|
787 });
|
|
788
|
|
789 /**
|
|
790 * Arguments to pass when POST method is used
|
|
791 * @attribute postData
|
|
792 * @default null
|
|
793 */
|
|
794 this.setAttributeConfig(POST_DATA, {
|
|
795 value: attr.postData || null
|
|
796 });
|
|
797
|
|
798 /**
|
|
799 * Whether or not the tab is currently active.
|
|
800 * If a dataSrc is set for the tab, the content will be loaded from
|
|
801 * the given source.
|
|
802 * @attribute active
|
|
803 * @type Boolean
|
|
804 */
|
|
805 this.setAttributeConfig('active', {
|
|
806 value: attr.active || this.hasClass(this.ACTIVE_CLASSNAME),
|
|
807 method: function(value) {
|
|
808 if (value === true) {
|
|
809 this.addClass(this.ACTIVE_CLASSNAME);
|
|
810 this.set('title', this.ACTIVE_TITLE);
|
|
811 } else {
|
|
812 this.removeClass(this.ACTIVE_CLASSNAME);
|
|
813 this.set('title', '');
|
|
814 }
|
|
815 },
|
|
816 validator: function(value) {
|
|
817 return Lang.isBoolean(value) && !this.get(DISABLED) ;
|
|
818 }
|
|
819 });
|
|
820
|
|
821 /**
|
|
822 * Whether or not the tab is disabled.
|
|
823 * @attribute disabled
|
|
824 * @type Boolean
|
|
825 */
|
|
826 this.setAttributeConfig(DISABLED, {
|
|
827 value: attr.disabled || this.hasClass(this.DISABLED_CLASSNAME),
|
|
828 method: function(value) {
|
|
829 if (value === true) {
|
|
830 Dom.addClass(this.get(ELEMENT), this.DISABLED_CLASSNAME);
|
|
831 } else {
|
|
832 Dom.removeClass(this.get(ELEMENT), this.DISABLED_CLASSNAME);
|
|
833 }
|
|
834 },
|
|
835 validator: Lang.isBoolean
|
|
836 });
|
|
837
|
|
838 /**
|
|
839 * The href of the tab's anchor element.
|
|
840 * @attribute href
|
|
841 * @type String
|
|
842 * @default '#'
|
|
843 */
|
|
844 this.setAttributeConfig('href', {
|
|
845 value: attr.href ||
|
|
846 this.getElementsByTagName('a')[0].getAttribute('href', 2) || '#',
|
|
847 method: function(value) {
|
|
848 this.getElementsByTagName('a')[0].href = value;
|
|
849 },
|
|
850 validator: Lang.isString
|
|
851 });
|
|
852
|
|
853 /**
|
|
854 * The Whether or not the tab's content is visible.
|
|
855 * @attribute contentVisible
|
|
856 * @type Boolean
|
|
857 * @default false
|
|
858 */
|
|
859 this.setAttributeConfig('contentVisible', {
|
|
860 value: attr.contentVisible,
|
|
861 method: function(value) {
|
|
862 if (value) {
|
|
863 Dom.removeClass(this.get(CONTENT_EL), this.HIDDEN_CLASSNAME);
|
|
864
|
|
865 if ( this.get(DATA_SRC) ) {
|
|
866 // load dynamic content unless already loading or loaded and caching
|
|
867 if ( !this._loading && !(this.get(DATA_LOADED) && this.get(CACHE_DATA)) ) {
|
|
868 this._dataConnect();
|
|
869 }
|
|
870 }
|
|
871 } else {
|
|
872 Dom.addClass(this.get(CONTENT_EL), this.HIDDEN_CLASSNAME);
|
|
873 }
|
|
874 },
|
|
875 validator: Lang.isBoolean
|
|
876 });
|
|
877 },
|
|
878
|
|
879 _dataConnect: function() {
|
|
880 if (!Y.Connect) {
|
|
881 return false;
|
|
882 }
|
|
883
|
|
884 Dom.addClass(this.get(CONTENT_EL).parentNode, this.LOADING_CLASSNAME);
|
|
885 this._loading = true;
|
|
886 this.dataConnection = Y.Connect.asyncRequest(
|
|
887 this.get(LOAD_METHOD),
|
|
888 this.get(DATA_SRC),
|
|
889 {
|
|
890 success: function(o) {
|
|
891 this.loadHandler.success.call(this, o);
|
|
892 this.set(DATA_LOADED, true);
|
|
893 this.dataConnection = null;
|
|
894 Dom.removeClass(this.get(CONTENT_EL).parentNode,
|
|
895 this.LOADING_CLASSNAME);
|
|
896 this._loading = false;
|
|
897 },
|
|
898 failure: function(o) {
|
|
899 this.loadHandler.failure.call(this, o);
|
|
900 this.dataConnection = null;
|
|
901 Dom.removeClass(this.get(CONTENT_EL).parentNode,
|
|
902 this.LOADING_CLASSNAME);
|
|
903 this._loading = false;
|
|
904 },
|
|
905 scope: this,
|
|
906 timeout: this.get(DATA_TIMEOUT)
|
|
907 },
|
|
908
|
|
909 this.get(POST_DATA)
|
|
910 );
|
|
911 },
|
|
912 _createTabElement: function(attr) {
|
|
913 var el = document.createElement('li'),
|
|
914 a = document.createElement('a'),
|
|
915 label = attr.label || null,
|
|
916 labelEl = attr.labelEl || null;
|
|
917
|
|
918 a.href = attr.href || '#'; // TODO: Use Dom.setAttribute?
|
|
919 el.appendChild(a);
|
|
920
|
|
921 if (labelEl) { // user supplied labelEl
|
|
922 if (!label) { // user supplied label
|
|
923 label = this._getLabel();
|
|
924 }
|
|
925 } else {
|
|
926 labelEl = this._createLabelEl();
|
|
927 }
|
|
928
|
|
929 a.appendChild(labelEl);
|
|
930
|
|
931 return el;
|
|
932 },
|
|
933
|
|
934 _getLabelEl: function() {
|
|
935 return this.getElementsByTagName(this.LABEL_TAGNAME)[0];
|
|
936 },
|
|
937
|
|
938 _createLabelEl: function() {
|
|
939 var el = document.createElement(this.LABEL_TAGNAME);
|
|
940 return el;
|
|
941 },
|
|
942
|
|
943
|
|
944 _getLabel: function() {
|
|
945 var el = this.get(LABEL_EL);
|
|
946
|
|
947 if (!el) {
|
|
948 return undefined;
|
|
949 }
|
|
950
|
|
951 return el.innerHTML;
|
|
952 },
|
|
953
|
|
954 _onActivate: function(e, tabview) {
|
|
955 var tab = this,
|
|
956 silent = false;
|
|
957
|
|
958 Y.Event.preventDefault(e);
|
|
959 if (tab === tabview.get(ACTIVE_TAB)) {
|
|
960 silent = true; // dont fire activeTabChange if already active
|
|
961 }
|
|
962 tabview.set(ACTIVE_TAB, tab, silent);
|
|
963 },
|
|
964
|
|
965 _onActivationEventChange: function(e) {
|
|
966 var tab = this;
|
|
967
|
|
968 if (e.prevValue != e.newValue) {
|
|
969 tab.removeListener(e.prevValue, tab._onActivate);
|
|
970 tab.addListener(e.newValue, tab._onActivate, this, tab);
|
|
971 }
|
|
972 }
|
|
973 });
|
|
974
|
|
975
|
|
976 /**
|
|
977 * Fires when a tab is removed from the tabview
|
|
978 * @event remove
|
|
979 * @type CustomEvent
|
|
980 * @param {Event} An event object with fields for "type" ("remove")
|
|
981 * and "tabview" (the tabview instance it was removed from)
|
|
982 */
|
|
983
|
|
984 YAHOO.widget.Tab = Tab;
|
|
985 })();
|
|
986
|
|
987 YAHOO.register("tabview", YAHOO.widget.TabView, {version: "2.8.0r4", build: "2449"});
|