tweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscordmessengercustom-servicesmacoslinuxwindowsinboxwhatsappicloud
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
721 lines
24 KiB
721 lines
24 KiB
/** |
|
* TabBar is used internally by a {@link Ext.tab.Panel TabPanel} and typically should not |
|
* need to be created manually. |
|
*/ |
|
Ext.define('Ext.tab.Bar', { |
|
extend: 'Ext.panel.Bar', |
|
xtype: 'tabbar', |
|
|
|
baseCls: Ext.baseCSSPrefix + 'tab-bar', |
|
|
|
requires: [ |
|
'Ext.tab.Tab', |
|
'Ext.util.Point', |
|
'Ext.layout.component.Body' |
|
], |
|
|
|
mixins: [ |
|
'Ext.util.FocusableContainer' |
|
], |
|
|
|
componentLayout: 'body', |
|
|
|
/** |
|
* @property {Boolean} isTabBar |
|
* `true` in this class to identify an object as an instantiated Tab Bar, or subclass thereof. |
|
*/ |
|
isTabBar: true, |
|
|
|
config: { |
|
/** |
|
* @cfg {'default'/0/1/2} tabRotation |
|
* The rotation of the tabs. Can be one of the following values: |
|
* |
|
* - `default` - use the default rotation, depending on the dock position (see below) |
|
* - `0` - no rotation |
|
* - `1` - rotate 90deg clockwise |
|
* - `2` - rotate 90deg counter-clockwise |
|
* |
|
* The default behavior of this config depends on the dock position: |
|
* |
|
* - `'top'` or `'bottom'` - `0` |
|
* - `'right'` - `1` |
|
* - `'left'` - `2` |
|
*/ |
|
tabRotation: 'default', |
|
|
|
/** |
|
* @cfg {Boolean} tabStretchMax |
|
* `true` to stretch all tabs to the height of the tallest tab when the tabBar |
|
* is docked horizontally, or the width of the widest tab when the tabBar is |
|
* docked vertically. |
|
*/ |
|
tabStretchMax: true, |
|
|
|
// NB: This option is named this way for the intent, but in fact activation |
|
// happens in arrow key handler, not in focus handler. In IE focus events are |
|
// asynchronous, so activation happens before the tab's focus handler is fired. |
|
/** |
|
* @cfg {Boolean} [activateOnFocus=true] |
|
* `true` to follow WAI-ARIA requirement and activate tab when it is navigated to |
|
* with arrow keys, or `false` to disable that behavior. When activation on focus |
|
* is disabled, users will have to use arrow keys to focus a tab, and then press |
|
* Space key to activate it. |
|
*/ |
|
activateOnFocus: true |
|
}, |
|
|
|
// @private |
|
defaultType: 'tab', |
|
|
|
/** |
|
* @cfg {Boolean} plain |
|
* True to not show the full background on the tabbar |
|
*/ |
|
plain: false, |
|
|
|
/** |
|
* @cfg {Boolean} ensureActiveVisibleOnChange |
|
* `true` to ensure the active tab is scrolled into view when the tab changes, the text, the |
|
* icon or the glyph. This is only applicable if using an overflow scroller. |
|
* |
|
* @since 5.1.1 |
|
*/ |
|
ensureActiveVisibleOnChange: true, |
|
|
|
ariaRole: 'tablist', |
|
|
|
childEls: [ |
|
'body', 'strip' |
|
], |
|
|
|
_stripCls: Ext.baseCSSPrefix + 'tab-bar-strip', |
|
_baseBodyCls: Ext.baseCSSPrefix + 'tab-bar-body', |
|
|
|
// @private |
|
renderTpl: |
|
'<div id="{id}-body" data-ref="body" role="presentation" class="{baseBodyCls} {baseBodyCls}-{ui} ' + |
|
'{bodyCls} {bodyTargetCls}{childElCls}"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>' + |
|
'{%this.renderContainer(out,values)%}' + |
|
'</div>' + |
|
'<div id="{id}-strip" data-ref="strip" role="presentation" class="{stripCls} {stripCls}-{ui}{childElCls}"></div>', |
|
|
|
/** |
|
* @cfg {Number} minTabWidth |
|
* The minimum width for a tab in this tab Bar. Defaults to the tab Panel's {@link Ext.tab.Panel#minTabWidth minTabWidth} value. |
|
* @deprecated This config is deprecated. It is much easier to use the {@link Ext.tab.Panel#minTabWidth minTabWidth} config on the TabPanel. |
|
*/ |
|
|
|
/** |
|
* @cfg {Number} maxTabWidth |
|
* The maximum width for a tab in this tab Bar. Defaults to the tab Panel's {@link Ext.tab.Panel#maxTabWidth maxTabWidth} value. |
|
* @deprecated This config is deprecated. It is much easier to use the {@link Ext.tab.Panel#maxTabWidth maxTabWidth} config on the TabPanel. |
|
*/ |
|
|
|
_reverseDockNames: { |
|
left: 'right', |
|
right: 'left' |
|
}, |
|
|
|
_layoutAlign: { |
|
top: 'end', |
|
right: 'begin', |
|
bottom: 'begin', |
|
left: 'end' |
|
}, |
|
|
|
/** |
|
* @event change |
|
* Fired when the currently-active tab has changed |
|
* @param {Ext.tab.Bar} tabBar The TabBar |
|
* @param {Ext.tab.Tab} tab The new Tab |
|
* @param {Ext.Component} card The card that was just shown in the TabPanel |
|
*/ |
|
|
|
// @private |
|
initComponent: function() { |
|
var me = this, |
|
initialLayout = me.initialConfig.layout, |
|
initialAlign = initialLayout && initialLayout.align, |
|
initialOverflowHandler = initialLayout && initialLayout.overflowHandler; |
|
|
|
if (me.plain) { |
|
me.addCls(me.baseCls + '-plain'); |
|
} |
|
|
|
me.callParent(); |
|
|
|
me.setLayout({ |
|
align: initialAlign || (me.getTabStretchMax() ? 'stretchmax' : |
|
me._layoutAlign[me.dock]), |
|
overflowHandler: initialOverflowHandler || 'scroller' |
|
}); |
|
|
|
me.on({ |
|
click: me.onClick, |
|
element: 'el', |
|
scope: me |
|
}); |
|
}, |
|
|
|
/** |
|
* Ensure the passed tab is visible if using overflow scrolling |
|
* @param {Ext.tab.Tab/Ext.Component/Number} [tab] The tab, item in the owning {@link Ext.tab.Panel} or |
|
* the index of the item to scroll to. Defaults to the active tab. |
|
*/ |
|
ensureTabVisible: function(tab) { |
|
var me = this, |
|
tabPanel = me.tabPanel, |
|
overflowHandler = me.layout.overflowHandler; |
|
|
|
if (me.rendered && overflowHandler && me.tooNarrow && overflowHandler.scrollToItem) { |
|
if (tab || tab === 0) { |
|
if (!tab.isTab) { |
|
if (Ext.isNumber(tab)) { |
|
tab = this.items.getAt(tab); |
|
} else if (tab.isComponent && tabPanel && tabPanel.items.contains(tab)) { |
|
tab = tab.tab; |
|
} |
|
} |
|
} |
|
|
|
if (!tab) { |
|
tab = me.activeTab; |
|
} |
|
|
|
if (tab) { |
|
overflowHandler.scrollToItem(tab); |
|
} |
|
} |
|
}, |
|
|
|
initRenderData: function() { |
|
var me = this; |
|
|
|
return Ext.apply(me.callParent(), { |
|
bodyCls: me.bodyCls, |
|
baseBodyCls: me._baseBodyCls, |
|
bodyTargetCls: me.bodyTargetCls, |
|
stripCls: me._stripCls, |
|
dock: me.dock |
|
}); |
|
}, |
|
|
|
setDock: function(dock) { |
|
var me = this, |
|
items = me.items, |
|
ownerCt = me.ownerCt, |
|
item, i, ln; |
|
|
|
items = items && items.items; |
|
|
|
if (items) { |
|
for (i = 0, ln = items.length; i < ln; i++) { |
|
item = items[i]; |
|
if (item.isTab) { |
|
item.setTabPosition(dock); |
|
} |
|
} |
|
} |
|
|
|
if (me.rendered) { |
|
// TODO: remove resetItemMargins once EXTJS-13359 is fixed |
|
me.resetItemMargins(); |
|
if (ownerCt && ownerCt.isHeader) { |
|
ownerCt.resetItemMargins(); |
|
} |
|
me.needsScroll = true; |
|
} |
|
me.callParent([dock]); |
|
}, |
|
|
|
updateTabRotation: function(tabRotation) { |
|
var me = this, |
|
items = me.items, |
|
i, ln, item; |
|
|
|
items = items && items.items; |
|
|
|
if (items) { |
|
for (i = 0, ln = items.length; i < ln; i++) { |
|
item = items[i]; |
|
if (item.isTab) { |
|
item.setRotation(tabRotation); |
|
} |
|
} |
|
} |
|
|
|
if (me.rendered) { |
|
// TODO: remove resetItemMargins once EXTJS-13359 is fixed |
|
me.resetItemMargins(); |
|
|
|
me.needsScroll = true; |
|
me.updateLayout(); |
|
} |
|
}, |
|
|
|
onRender: function() { |
|
var me = this; |
|
|
|
me.callParent(); |
|
|
|
if (Ext.isIE8 && me.vertical) { |
|
me.el.on({ |
|
mousemove: me.onMouseMove, |
|
scope: me |
|
}); |
|
} |
|
}, |
|
|
|
afterLayout: function() { |
|
this.adjustTabPositions(); |
|
this.callParent(arguments); |
|
}, |
|
|
|
onAdd: function(tab, pos) { |
|
var fn = this.onTabContentChange; |
|
|
|
if (this.ensureActiveVisibleOnChange) { |
|
tab.barListeners = tab.on({ |
|
scope: this, |
|
destroyable: true, |
|
glyphchange: fn, |
|
iconchange: fn, |
|
textchange: fn |
|
}); |
|
} |
|
this.callParent([tab, pos]); |
|
}, |
|
|
|
onAdded: function(container, pos, instanced) { |
|
if (container.isHeader) { |
|
this.addCls(container.baseCls + '-' + container.ui + '-tab-bar'); |
|
} |
|
this.callParent([container, pos, instanced]); |
|
}, |
|
|
|
onRemove: function(tab, destroying) { |
|
var me = this; |
|
|
|
// If we're not destroying, no need to do this here since they will |
|
// be cleaned up |
|
if (me.ensureActiveVisibleOnChange) { |
|
if (!destroying) { |
|
tab.barListeners.destroy(); |
|
} |
|
tab.barListeners = null; |
|
} |
|
|
|
if (tab === me.previousTab) { |
|
me.previousTab = null; |
|
} |
|
me.callParent([tab, destroying]); |
|
}, |
|
|
|
onRemoved: function(destroying) { |
|
var ownerCt = this.ownerCt; |
|
|
|
if (ownerCt.isHeader) { |
|
this.removeCls(ownerCt.baseCls + '-' + ownerCt.ui + '-tab-bar'); |
|
} |
|
this.callParent([destroying]); |
|
}, |
|
|
|
onTabContentChange: function(tab) { |
|
if (tab === this.activeTab) { |
|
this.ensureTabVisible(tab); |
|
} |
|
}, |
|
|
|
afterComponentLayout: function(width) { |
|
var me = this, |
|
needsScroll = me.needsScroll, |
|
overflowHandler = me.layout.overflowHandler; |
|
|
|
me.callParent(arguments); |
|
|
|
if (overflowHandler && needsScroll && me.tooNarrow && overflowHandler.scrollToItem) { |
|
overflowHandler.scrollToItem(me.activeTab); |
|
} |
|
delete me.needsScroll; |
|
}, |
|
|
|
// private |
|
onMouseMove: function(e) { |
|
var me = this, |
|
overTab = me._overTab, |
|
tabInfo, tab; |
|
|
|
if (e.getTarget('.' + Ext.baseCSSPrefix + 'box-scroller')) { |
|
return; |
|
} |
|
|
|
tabInfo = me.getTabInfoFromPoint(e.getXY()); |
|
tab = tabInfo.tab; |
|
|
|
if (tab !== overTab) { |
|
if (overTab && overTab.rendered) { |
|
overTab.onMouseLeave(e); |
|
me._overTab = null; |
|
} |
|
if (tab) { |
|
tab.onMouseEnter(e); |
|
me._overTab = tab; |
|
if (!tab.disabled) { |
|
me.el.setStyle('cursor', 'pointer'); |
|
} |
|
} else { |
|
me.el.setStyle('cursor', 'default'); |
|
} |
|
} |
|
}, |
|
|
|
onMouseLeave: function(e) { |
|
var overTab = this._overTab; |
|
|
|
if (overTab && overTab.rendered) { |
|
overTab.onMouseLeave(e); |
|
} |
|
}, |
|
|
|
// @private |
|
// in IE8 and IE9 the clickable region of a rotated element is not its new rotated |
|
// position, but it's original unrotated position. The result is that rotated tabs do |
|
// not capture click and mousenter/mosueleave events correctly. This method accepts |
|
// an xy position and calculates if the coordinates are within a tab and if they |
|
// are within the tab's close icon (if any) |
|
getTabInfoFromPoint: function(xy) { |
|
var me = this, |
|
tabs = me.items.items, |
|
length = tabs.length, |
|
innerCt = me.layout.innerCt, |
|
innerCtXY = innerCt.getXY(), |
|
point = new Ext.util.Point(xy[0], xy[1]), |
|
i = 0, |
|
lastBox, tabRegion, closeEl, close, closeXY, closeX, closeY, closeWidth, |
|
closeHeight, tabX, tabY, tabWidth, tabHeight, closeRegion, isTabReversed, |
|
direction, tab; |
|
|
|
for (; i < length; i++) { |
|
tab = tabs[i]; |
|
lastBox = tab.lastBox; |
|
if (!lastBox || !tab.isTab) { |
|
// avoid looping hidden or not laid out, or if the item |
|
// is not a tab |
|
continue; |
|
} |
|
tabX = innerCtXY[0] + lastBox.x; |
|
tabY = innerCtXY[1] - innerCt.dom.scrollTop + lastBox.y; |
|
tabWidth = lastBox.width; |
|
tabHeight = lastBox.height; |
|
tabRegion = new Ext.util.Region( |
|
tabY, |
|
tabX + tabWidth, |
|
tabY + tabHeight, |
|
tabX |
|
); |
|
if (tabRegion.contains(point)) { |
|
closeEl = tab.closeEl; |
|
if (closeEl) { |
|
// Read the dom to determine if the contents of the tab are reversed |
|
// (rotated 180 degrees). If so, we can cache the result becuase |
|
// it's safe to assume all tabs in the tabbar will be the same |
|
if (me._isTabReversed === undefined) { |
|
me._isTabReversed = isTabReversed = |
|
// use currentStyle because getComputedStyle won't get the |
|
// filter property in IE9 |
|
(tab.btnWrap.dom.currentStyle.filter.indexOf('rotation=2') !== -1); |
|
} |
|
|
|
direction = isTabReversed ? this._reverseDockNames[me.dock] : me.dock; |
|
|
|
closeWidth = closeEl.getWidth(); |
|
closeHeight = closeEl.getHeight(); |
|
closeXY = me.getCloseXY(closeEl, tabX, tabY, tabWidth, tabHeight, |
|
closeWidth, closeHeight, direction); |
|
closeX = closeXY[0]; |
|
closeY = closeXY[1]; |
|
|
|
closeRegion = new Ext.util.Region( |
|
closeY, |
|
closeX + closeWidth, |
|
closeY + closeHeight, |
|
closeX |
|
); |
|
|
|
close = closeRegion.contains(point); |
|
} |
|
break; |
|
} |
|
} |
|
|
|
return { |
|
tab: tab, |
|
close: close |
|
}; |
|
}, |
|
|
|
// @private |
|
getCloseXY: function(closeEl, tabX, tabY, tabWidth, tabHeight, closeWidth, closeHeight, direction) { |
|
var closeXY = closeEl.getXY(), |
|
closeX, closeY; |
|
|
|
if (direction === 'right') { |
|
closeX = tabX + tabWidth - ((closeXY[1] - tabY) + closeHeight); |
|
closeY = tabY + (closeXY[0] - tabX); |
|
} else { |
|
closeX = tabX + (closeXY[1] - tabY); |
|
closeY = tabY + tabX + tabHeight - closeXY[0] - closeWidth; |
|
} |
|
|
|
return [closeX, closeY]; |
|
}, |
|
|
|
/** |
|
* @private |
|
* Closes the given tab by removing it from the TabBar and removing the corresponding card from the TabPanel |
|
* @param {Ext.tab.Tab} toClose The tab to close |
|
*/ |
|
closeTab: function(toClose) { |
|
var me = this, |
|
card = toClose.card, |
|
tabPanel = me.tabPanel, |
|
toActivate; |
|
|
|
if (card && card.fireEvent('beforeclose', card) === false) { |
|
return false; |
|
} |
|
|
|
// If we are closing the active tab, revert to the previously active tab (or the previous or next enabled sibling if |
|
// there *is* no previously active tab, or the previously active tab is the one that's being closed or the previously |
|
// active tab has since been disabled) |
|
toActivate = me.findNextActivatable(toClose); |
|
|
|
// We are going to remove the associated card, and then, if that was sucessful, remove the Tab, |
|
// And then potentially activate another Tab. We should not layout for each of these operations. |
|
Ext.suspendLayouts(); |
|
|
|
if (tabPanel && card) { |
|
// Remove the ownerCt so the tab doesn't get destroyed if the remove is successful |
|
// We need this so we can have the tab fire it's own close event. |
|
delete toClose.ownerCt; |
|
|
|
// we must fire 'close' before removing the card from panel, otherwise |
|
// the event will no loger have any listener |
|
card.fireEvent('close', card); |
|
tabPanel.remove(card); |
|
|
|
// Remove succeeded |
|
if (!tabPanel.getComponent(card)) { |
|
/* |
|
* Force the close event to fire. By the time this function returns, |
|
* the tab is already destroyed and all listeners have been purged |
|
* so the tab can't fire itself. |
|
*/ |
|
toClose.fireClose(); |
|
me.remove(toClose); |
|
} else { |
|
// Restore the ownerCt from above |
|
toClose.ownerCt = me; |
|
Ext.resumeLayouts(true); |
|
return false; |
|
} |
|
} |
|
|
|
// If we are closing the active tab, revert to the previously active tab (or the previous sibling or the nnext sibling) |
|
if (toActivate) { |
|
// Our owning TabPanel calls our setActiveTab method, so only call that if this Bar is being used |
|
// in some other context (unlikely) |
|
if (tabPanel) { |
|
tabPanel.setActiveTab(toActivate.card); |
|
} else { |
|
me.setActiveTab(toActivate); |
|
} |
|
toActivate.focus(); |
|
} |
|
Ext.resumeLayouts(true); |
|
}, |
|
|
|
// private - used by TabPanel too. |
|
// Works out the next tab to activate when one tab is closed. |
|
findNextActivatable: function(toClose) { |
|
var me = this; |
|
if (toClose.active && me.items.getCount() > 1) { |
|
return (me.previousTab && me.previousTab !== toClose && !me.previousTab.disabled) ? me.previousTab : (toClose.next('tab[disabled=false]') || toClose.prev('tab[disabled=false]')); |
|
} |
|
}, |
|
|
|
/** |
|
* @private |
|
* Marks the given tab as active |
|
* @param {Ext.tab.Tab} tab The tab to mark active |
|
* @param {Boolean} initial True if we're setting the tab during setup |
|
*/ |
|
setActiveTab: function(tab, initial) { |
|
var me = this; |
|
|
|
if (!tab.disabled && tab !== me.activeTab) { |
|
// Deactivate the previous tab, and ensure this FocusableContainer knows about it |
|
if (me.activeTab) { |
|
if (me.activeTab.isDestroyed) { |
|
me.previousTab = null; |
|
} else { |
|
me.previousTab = me.activeTab; |
|
me.activeTab.deactivate(); |
|
me.deactivateFocusable(me.activeTab); |
|
} |
|
} |
|
|
|
// Activate the new tab, and ensure this FocusableContainer knows about it |
|
tab.activate(); |
|
me.activateFocusable(tab); |
|
|
|
me.activeTab = tab; |
|
me.needsScroll = true; |
|
|
|
// We don't fire the change event when setting the first tab. |
|
// Also no need to run a layout |
|
if (!initial) { |
|
me.fireEvent('change', me, tab, tab.card); |
|
// Ensure that after the currently in progress layout, the active tab is scrolled into view |
|
me.updateLayout(); |
|
} |
|
} |
|
}, |
|
|
|
privates: { |
|
adjustTabPositions: function() { |
|
var me = this, |
|
items = me.items.items, |
|
i = items.length, |
|
tab, lastBox, el, rotation, prop; |
|
|
|
// When tabs are rotated vertically we don't have a reliable way to position |
|
// them using CSS in modern browsers. This is because of the way transform-orign |
|
// works - it requires the width to be known, and the width is not known in css. |
|
// Consequently we have to make an adjustment to the tab's position in these browsers. |
|
// This is similar to what we do in Ext.panel.Header#adjustTitlePosition |
|
if (!Ext.isIE8) { |
|
// 'left' in normal mode, 'right' in rtl |
|
prop = me._getTabAdjustProp(); |
|
|
|
while (i--) { |
|
tab = items[i]; |
|
el = tab.el; |
|
lastBox = tab.lastBox; |
|
rotation = tab.isTab ? tab.getActualRotation() : 0; |
|
if (rotation === 1 && tab.isVisible()) { |
|
// rotated 90 degrees using the top left corner as the axis. |
|
// tabs need to be shifted to the right by their width |
|
el.setStyle(prop, (lastBox.x + lastBox.width) + 'px'); |
|
} else if (rotation === 2 && tab.isVisible()) { |
|
// rotated 270 degrees using the bottom right corner as the axis. |
|
// tabs need to be shifted to the left by their height |
|
el.setStyle(prop, (lastBox.x - lastBox.height) + 'px'); |
|
} |
|
} |
|
} |
|
}, |
|
|
|
applyTargetCls: function(targetCls) { |
|
this.bodyTargetCls = targetCls; |
|
}, |
|
|
|
// rtl hook |
|
_getTabAdjustProp: function() { |
|
return 'left'; |
|
}, |
|
|
|
getTargetEl: function() { |
|
return this.body || this.frameBody || this.el; |
|
}, |
|
|
|
onClick: function(e, target) { |
|
var me = this, |
|
tabEl, tab, isCloseClick, tabInfo; |
|
|
|
if (e.getTarget('.' + Ext.baseCSSPrefix + 'box-scroller')) { |
|
return; |
|
} |
|
|
|
if (Ext.isIE8 && me.vertical) { |
|
tabInfo = me.getTabInfoFromPoint(e.getXY()); |
|
tab = tabInfo.tab; |
|
isCloseClick = tabInfo.close; |
|
} else { |
|
// The target might not be a valid tab el. |
|
tabEl = e.getTarget('.' + Ext.tab.Tab.prototype.baseCls); |
|
tab = tabEl && Ext.getCmp(tabEl.id); |
|
isCloseClick = tab && tab.closeEl && (target === tab.closeEl.dom); |
|
} |
|
|
|
if (isCloseClick) { |
|
e.preventDefault(); |
|
} |
|
|
|
if (tab && tab.isDisabled && !tab.isDisabled()) { |
|
// This will focus the tab; we do it before activating the card |
|
// because the card may attempt to focus itself or a child item. |
|
// We need to focus the tab explicitly because click target is |
|
// the Bar, not the Tab. |
|
tab.beforeClick(isCloseClick); |
|
|
|
if (tab.closable && isCloseClick) { |
|
tab.onCloseClick(); |
|
} |
|
else { |
|
me.doActivateTab(tab); |
|
} |
|
} |
|
}, |
|
|
|
doActivateTab: function(tab) { |
|
var tabPanel = this.tabPanel; |
|
|
|
if (tabPanel) { |
|
// TabPanel will call setActiveTab of the TabBar |
|
if (!tab.disabled) { |
|
tabPanel.setActiveTab(tab.card); |
|
} |
|
} else { |
|
this.setActiveTab(tab); |
|
} |
|
}, |
|
|
|
onFocusableContainerFocus: function(e) { |
|
var me = this, |
|
mixin = me.mixins.focusablecontainer, |
|
child; |
|
|
|
child = mixin.onFocusableContainerFocus.call(me, e); |
|
|
|
if (child && child.isTab) { |
|
me.doActivateTab(child); |
|
} |
|
}, |
|
|
|
onFocusableContainerFocusEnter: function(e) { |
|
var me = this, |
|
mixin = me.mixins.focusablecontainer, |
|
child; |
|
|
|
child = mixin.onFocusableContainerFocusEnter.call(me, e); |
|
|
|
if (child && child.isTab) { |
|
me.doActivateTab(child); |
|
} |
|
}, |
|
|
|
focusChild: function(child, forward) { |
|
var me = this, |
|
mixin = me.mixins.focusablecontainer, |
|
nextChild; |
|
|
|
nextChild = mixin.focusChild.call(me, child, forward); |
|
|
|
if (me.activateOnFocus && nextChild && nextChild.isTab) { |
|
me.doActivateTab(nextChild); |
|
} |
|
} |
|
} |
|
});
|
|
|