<!DOCTYPE html>
|
<html>
|
<head>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<title>The source code</title>
|
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
|
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
|
<style type="text/css">
|
.highlight { display: block; background-color: #ddd; }
|
</style>
|
<script type="text/javascript">
|
function highlight() {
|
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
|
}
|
</script>
|
</head>
|
<body onload="prettyPrint(); highlight();">
|
<pre class="prettyprint lang-js"><span id='Ext-tab-Bar'>/**
|
</span> * @author Ed Spencer
|
* TabBar is used internally by a {@link Ext.tab.Panel TabPanel} and typically should not need to be created manually.
|
* The tab bar automatically removes the default title provided by {@link Ext.panel.Header}
|
*/
|
Ext.define('Ext.tab.Bar', {
|
extend: 'Ext.panel.Header',
|
alias: 'widget.tabbar',
|
<span id='Ext-tab-Bar-cfg-baseCls'> baseCls: Ext.baseCSSPrefix + 'tab-bar',
|
</span>
|
requires: [
|
'Ext.tab.Tab',
|
'Ext.util.Point'
|
],
|
|
<span id='Ext-tab-Bar-property-isTabBar'> /**
|
</span> * @property {Boolean} isTabBar
|
* `true` in this class to identify an object as an instantiated Tab Bar, or subclass thereof.
|
*/
|
isTabBar: true,
|
|
<span id='Ext-tab-Bar-cfg-title'> /**
|
</span> * @cfg {String} title @hide
|
*/
|
|
<span id='Ext-tab-Bar-cfg-iconCls'> /**
|
</span> * @cfg {String} iconCls @hide
|
*/
|
|
<span id='Ext-tab-Bar-cfg-defaultType'> // @private
|
</span> defaultType: 'tab',
|
|
<span id='Ext-tab-Bar-cfg-plain'> /**
|
</span> * @cfg {Boolean} plain
|
* True to not show the full background on the tabbar
|
*/
|
plain: false,
|
|
<span id='Ext-tab-Bar-property-childEls'> childEls: [
|
</span> 'body', 'strip'
|
],
|
|
<span id='Ext-tab-Bar-cfg-renderTpl'> // @private
|
</span> renderTpl: [
|
'<div id="{id}-body" class="{baseCls}-body {bodyCls} {bodyTargetCls}{childElCls}<tpl if="ui"> {baseCls}-body-{ui}<tpl for="uiCls"> {parent.baseCls}-body-{parent.ui}-{.}</tpl></tpl>"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>',
|
'{%this.renderContainer(out,values)%}',
|
'</div>',
|
'<div id="{id}-strip" class="{baseCls}-strip {baseCls}-strip-{dock}{childElCls}',
|
'<tpl if="ui"> {baseCls}-strip-{ui}',
|
'<tpl for="uiCls"> {parent.baseCls}-strip-{parent.ui}-{.}</tpl>',
|
'</tpl>">',
|
'</div>'
|
],
|
|
<span id='Ext-tab-Bar-cfg-minTabWidth'> /**
|
</span> * @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.
|
*/
|
|
<span id='Ext-tab-Bar-cfg-maxTabWidth'> /**
|
</span> * @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'
|
},
|
|
<span id='Ext-tab-Bar-method-initComponent'> // @private
|
</span> initComponent: function() {
|
var me = this;
|
|
if (me.plain) {
|
me.addCls(me.baseCls + '-plain');
|
}
|
|
me.addClsWithUI(me.orientation);
|
|
me.addEvents(
|
<span id='Ext-tab-Bar-event-change'> /**
|
</span> * @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
|
*/
|
'change'
|
);
|
|
// Element onClick listener added by Header base class
|
me.callParent(arguments);
|
Ext.merge(me.layout, me.initialConfig.layout);
|
|
// TabBar must override the Header's align setting.
|
me.layout.align = (me.orientation == 'vertical') ? 'left' : 'top';
|
me.layout.overflowHandler = new Ext.layout.container.boxOverflow.Scroller(me.layout);
|
|
me.remove(me.titleCmp);
|
delete me.titleCmp;
|
|
Ext.apply(me.renderData, {
|
bodyCls: me.bodyCls,
|
dock: me.dock
|
});
|
},
|
|
<span id='Ext-tab-Bar-method-onRender'> onRender: function() {
|
</span> var me = this;
|
|
me.callParent();
|
|
if (me.orientation === 'vertical' && (Ext.isIE8 || Ext.isIE9) && Ext.isStrict) {
|
me.el.on({
|
mousemove: me.onMouseMove,
|
scope: me
|
});
|
}
|
},
|
|
<span id='Ext-tab-Bar-method-afterRender'> afterRender: function() {
|
</span> var layout = this.layout;
|
|
this.callParent();
|
if (Ext.isIE9 && Ext.isStrict && this.orientation === 'vertical') {
|
// EXTJSIV-8765: focusing a vertically-oriented tab in IE9 strict can cause
|
// the innerCt to scroll if the tabs have bordering.
|
layout.innerCt.on('scroll', function() {
|
layout.innerCt.dom.scrollLeft = 0;
|
});
|
}
|
},
|
|
<span id='Ext-tab-Bar-method-afterLayout'> afterLayout: function() {
|
</span> this.adjustTabPositions();
|
this.callParent(arguments);
|
},
|
|
<span id='Ext-tab-Bar-method-adjustTabPositions'> adjustTabPositions: function() {
|
</span> var items = this.items.items,
|
i = items.length,
|
tab;
|
|
// 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.isIE9m) {
|
if (this.dock === 'right') {
|
// rotated 90 degrees around using the top left corner as the axis.
|
// tabs need to be shifted to the right by their width
|
while (i--) {
|
tab = items[i];
|
if (tab.isVisible()) {
|
tab.el.setStyle('left', tab.lastBox.width + 'px');
|
}
|
}
|
} else if (this.dock === 'left') {
|
// rotated 270 degrees around using the top left corner as the axis.
|
// tabs need to be shifted down by their height
|
while (i--) {
|
tab = items[i];
|
if (tab.isVisible()) {
|
tab.el.setStyle('left', -tab.lastBox.height + 'px');
|
}
|
}
|
}
|
}
|
},
|
|
<span id='Ext-tab-Bar-method-getLayout'> getLayout: function() {
|
</span> var me = this;
|
me.layout.type = (me.orientation === 'horizontal') ? 'hbox' : 'vbox';
|
return me.callParent(arguments);
|
},
|
|
<span id='Ext-tab-Bar-method-onAdd'> // @private
|
</span> onAdd: function(tab) {
|
tab.position = this.dock;
|
this.callParent(arguments);
|
},
|
|
<span id='Ext-tab-Bar-method-onRemove'> onRemove: function(tab) {
|
</span> var me = this;
|
|
if (tab === me.previousTab) {
|
me.previousTab = null;
|
}
|
me.callParent(arguments);
|
},
|
|
<span id='Ext-tab-Bar-method-afterComponentLayout'> afterComponentLayout : function(width) {
|
</span> var me = this,
|
needsScroll = me.needsScroll;
|
|
me.callParent(arguments);
|
|
if (needsScroll) {
|
me.layout.overflowHandler.scrollToItem(me.activeTab);
|
}
|
delete me.needsScroll;
|
},
|
|
<span id='Ext-tab-Bar-method-onClick'> // @private
|
</span> onClick: function(e, target) {
|
var me = this,
|
tabPanel = me.tabPanel,
|
tabEl, tab, isCloseClick, tabInfo;
|
|
if (e.getTarget('.' + Ext.baseCSSPrefix + 'box-scroller')) {
|
return;
|
}
|
|
if (me.orientation === 'vertical' && (Ext.isIE8 || Ext.isIE9) && Ext.isStrict) {
|
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()) {
|
if (tab.closable && isCloseClick) {
|
tab.onCloseClick();
|
} else {
|
if (tabPanel) {
|
// TabPanel will card setActiveTab of the TabBar
|
tabPanel.setActiveTab(tab.card);
|
} else {
|
me.setActiveTab(tab);
|
}
|
tab.focus();
|
}
|
}
|
},
|
|
<span id='Ext-tab-Bar-method-onMouseMove'> // private
|
</span> 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');
|
}
|
}
|
},
|
|
<span id='Ext-tab-Bar-method-onMouseLeave'> onMouseLeave: function(e) {
|
</span> var overTab = this._overTab;
|
|
if (overTab && overTab.rendered) {
|
overTab.onMouseLeave(e);
|
}
|
},
|
|
<span id='Ext-tab-Bar-method-getTabInfoFromPoint'> // @private
|
</span> // 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++) {
|
lastBox = tabs[i].lastBox;
|
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)) {
|
tab = tabs[i];
|
closeEl = tab.closeEl;
|
if (closeEl) {
|
closeXY = closeEl.getXY();
|
closeWidth = closeEl.getWidth();
|
closeHeight = closeEl.getHeight();
|
// 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;
|
|
if (direction === 'right') {
|
closeX = tabX + tabWidth - ((closeXY[1] - tabY) + closeEl.getHeight());
|
closeY = tabY + (closeXY[0] - tabX);
|
} else {
|
closeX = tabX + (closeXY[1] - tabY);
|
closeY = tabY + tabX + tabHeight - closeXY[0] - closeEl.getWidth();
|
}
|
|
closeRegion = new Ext.util.Region(
|
closeY,
|
closeX + closeWidth,
|
closeY + closeHeight,
|
closeX
|
);
|
|
close = closeRegion.contains(point);
|
}
|
break;
|
}
|
}
|
|
return {
|
tab: tab,
|
close: close
|
};
|
},
|
|
<span id='Ext-tab-Bar-method-closeTab'> /**
|
</span> * @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);
|
},
|
|
<span id='Ext-tab-Bar-method-findNextActivatable'> // private - used by TabPanel too.
|
</span> // 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]'));
|
}
|
},
|
|
<span id='Ext-tab-Bar-method-setActiveTab'> /**
|
</span> * @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) {
|
if (me.activeTab) {
|
if (me.activeTab.isDestroyed) {
|
me.previousTab = null;
|
} else {
|
me.previousTab = me.activeTab;
|
me.activeTab.deactivate();
|
}
|
}
|
tab.activate();
|
|
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();
|
}
|
}
|
}
|
});
|
</pre>
|
</body>
|
</html>
|