/*
* Copyright 2015 Anyware Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This class is the inside of the ribbon. You have to create it through the wrapper Ametys.ui.fluent.ribbon.Ribbon.
*
* Items are {@link Ametys.ui.fluent.ribbon.Panel} and can be classic tabs or contextual tabs.
*
*/
Ext.define(
"Ametys.ui.fluent.ribbon.TabPanel",
{
extend: "Ext.tab.Panel",
alias: 'widget.ametys.ribbon-tabpanel',
/**
* @cfg {String} [ui="ribbon-tabpanel"] @inheritdoc
*/
ui: 'ribbon-tabpanel',
/**
* @cfg {String} defaultType Doesn't apply to ribbon element.
* @private
*/
defaultType: 'ametys.ribbon-panel',
/**
* @cfg {Object} mainButton The configuration or object that will be added to #cfg-tools of the header at the left of the tabs.
*/
/**
* @cfg {Object[]/Ext.panel.Tool[]} tools tools Doesn't apply to ribbon element.
* @private
*/
/**
* @property {String} contextualTabCls The CSS classname to set on the button of contextual tabs
* @readonly
* @private
*/
contextualTabCls: 'a-fluent-tab-contextual',
/**
* @property {String} contextualTabWithNextInSameGroupCls The CSS classname to set on the button of contextual tabs when there is another visible member of the group after it
* @readonly
* @private
*/
contextualTabWithNextInSameGroupCls: 'a-fluent-tab-contextual-hasNextInGroup',
/**
* @private
* @property {String} contextualTabWithNextInSameGroupSeparation The size to separate the a visible contextual tab with its next visible contextual tab of the same group
*/
contextualTabWithNextInSameGroupSeparation: '0 1px 0 0',
/**
* @private
* @property {Ametys.ui.fluent.ribbon.Panel} _lastContextualTabHidden Last contextual panel selected. Null when hidden for too long.
*/
_lastContextualTabHidden: null,
/**
* @private
* @property {String} defaultTabSeparation The default separation
*/
defaultTabSeparation: '0 5px 0 0',
/**
* @private
* @readonly
* @property {String} _nonContextualToContextualSeparation The size to separate the last non-contextual with the first contextual
*/
nonContextualToContextualSeparation: '0 40px 0 0',
/**
* @private
* @property {Boolean} _displaySpaceAfterLastNonContextual Is the margin of #nonContextualToContextualSeparation currently visible?
*/
_displaySpaceAfterLastNonContextual: false,
/**
* Creates a TabPanel
* @param {Object} config The configuration
*/
constructor: function (config)
{
this._processShowHide = Ext.Function.createBuffered(this._processShowHide, 1, this);
/**
* @private
* @property {Object} _contextualGroups Map<String, Ametys.ui.fluent.ribbon.Panel[]>
* The map of contextual group id and the associated tabs
*/
this._contextualGroups = {};
/**
* @private
* @property {Object} _scaleGrid The map of the groups organized by size.
* The map is a Map<Number index, Object o>
* index is the index of the tab.
* o is an object with 2 properties:
* current a Number representing the index of the currently selected matrix
* matrix an Array[Object o2] where o2 is an object with the following properties:
* resize an Array[String s]. Its size is the same as the tabs one, its values can be "small", "medium" or "large". The array represents the possible combination of "small" "medium" and "large" groups for a tab
* width is a Number representing the size in pixel of the tab.
* groupsWidth an Array[Object o3] where o3 is for each group an object containing the different Ametys.ui.fluent.ribbon.Group.SIZE_* associated with the corresponding widths
*/
this._scaleGrid = {};
config = config || {};
config.tools = [];
// We want that tabs goes into header line : with an invisible title
var titlePosition = 1;
config.title = {
text: '',
flex: 0
};
config.tabBarHeaderPosition = 1;
config.tabBar = config.tabBar || {};
config.tabBar.flex = 1;
config.tabBar.layout = {
type: 'hbox',
overflowHandler: 'scroller'
};
// Handle main button
if (config.mainButton)
{
titlePosition = 2;
config.tabBarHeaderPosition = 2;
config.mainButton.ui = 'ribbon-tabpanel-mainbutton';
config.tools.push(config.mainButton);
}
config.tools.push({
xtype: 'tbspacer',
width: 3
});
// let's move title to somewhere it won't be annoying
config.header = {
titlePosition: titlePosition
};
this.callParent([config]);
this.on('mousewheel', this._onTabWheel, this, { element: 'el' });
// Fix because default position is quite strange
this.setPosition(0, 0);
},
/**
* @private
* Do initialization after rendering
*/
afterRender: function()
{
this.callParent(arguments);
this.items.each(this._panelAddedAfterRender, this);
},
setActiveTab: function(card)
{
var previous;
// Check for a config object
if (!Ext.isObject(card) || card.isComponent)
{
card = this.getComponent(card);
}
previous = this.getActiveTab();
if (card)
{
Ext.suspendLayouts();
try
{
card = this.callParent(arguments);
// We want to add contextual CSS classes to roots element
if (previous && previous.contextualTab)
{
this.removeCls(this.contextualTabCls + "-" + previous.contextualTab);
this.floatParent.removeCls(this.contextualTabCls + "-" + previous.contextualTab);
}
if (card.contextualTab)
{
this.addCls(this.contextualTabCls + "-" + card.contextualTab);
this.floatParent.addCls(this.contextualTabCls + "-" + card.contextualTab);
}
}
finally
{
Ext.resumeLayouts(true);
}
// We want to show the ribbon if it is collapsed
if (previous !== card)
{
if (this.floatParent._ribbonCollapsed)
{
this.floatParent._showCollapsed();
}
this.updateLayout();
}
}
return card;
},
afterComponentLayout: function(width, height, oldWidth, oldHeight)
{
this.callParent(arguments);
this._checkSize();
},
onResize: function(width, height, oldWidth, oldHeight)
{
this.callParent(arguments);
if (height != oldHeight)
{
this.floatParent._setRibbonHeight();
}
if (width != oldWidth)
{
this._checkSize();
}
},
/**
* Listener on adding tab to check for contextual tabs (thoses with the contextualTab property).
* See class doc to see the properties
* @param {Ametys.ui.fluent.ribbon.Panel} panel The panel added
* @param {Number} index The new tab index
*/
_panelAddedAfterRender: function(panel, index)
{
if (panel.contextualTab != null)
{
var tabElBtn = this.getTabBar().items.get(index);
// Generates a group if no group is specified (the tab will be alone in this group)
if (panel.contextualGroup == null)
{
panel.contextualGroup = Ext.id();
}
// Hide the tab button
tabElBtn.hide();
tabElBtn.contextual = true;
// Color the tab button
tabElBtn.addCls([this.contextualTabCls, this.contextualTabCls + "-" + panel.contextualTab]);
// Creates the group if the tab is the first one of the group
if (this._contextualGroups[panel.contextualGroup] == null)
{
this._contextualGroups[panel.contextualGroup] = [];
}
// Add the tab to the existing group
panel._contextualTabGroup = this._contextualGroups[panel.contextualGroup];
// Add the reference to the tab in the group label object
panel._contextualTabGroup.push(panel);
// Add shortcuts method for showing/hiding the contextual tab
panel.showContextualTab = function(forceSelection)
{
this.ownerCt.showContextualTab(this, forceSelection);
}
panel.hideContextualTab = function()
{
this.ownerCt.hideContextualTab(this);
}
}
else
{
// checking that non contextual tabs must not be after contextual
for (var i = index - 1; i >= 0; i--)
{
var tabEl = this.items.get(i);
if (!tabEl.hasCls(this.contextualTabCls))
{
if (i + 1 != index)
{
tabElBtn.dom.parentNode.insertBefore(tabElBtn.dom, this.items.get(i + 1));
}
break;
}
}
}
},
onAdd: function(panel, index)
{
this.callParent(arguments);
if (this.rendered)
{
this._panelAddedAfterRender(panel, index);
}
},
/**
* @private
* @property {Object} _toShowHide The list of buffered actions. The special key '_activate_' contains the tab (or tab index) to activate. While other keys are component ids and associated values are the action (between 'hide' or 'show')
*/
_toShowHide: {},
/**
* @private
* @property {Boolean} _pauseProcessing When true the process to show/hide contextual tabs is paused
*/
_pauseProcessing: false,
/**
* @private
* Is this tab element going to be visible?
* @param {Ext.Component} tabEl The tab button
* @return {Boolean} If the tab has bufferd 'show', returns true ; if the tab has a buffered 'hide' return false ; else returns current tab visibility
*/
_isTabVisibleNowOrSoon: function(tabEl)
{
var soon = this._toShowHide[tabEl.getId()];
if (soon == 'show')
{
return true;
}
else if (soon == 'hide')
{
return false;
}
else
{
return tabEl.isVisible();
}
},
/**
* @private
* Get the tab that is going to be the active one, or else the currently active
* @return {Ext.Component} A tab
*/
_getActiveTabNowOrSoon: function()
{
var futureTab = this._toShowHide['_activate_'];
if (Ext.isNumber(futureTab))
{
return this.items.get(futureTab)
}
else if (futureTab)
{
return futureTab;
}
else
{
return this.getActiveTab();
}
},
/**
* @private
* Buffer a call to setActiveTab/hide/show to avoid blinking
* @param {Number|Ext.Component} element The element to act. show/hive works only with a component ; while setActiveTab works with a component or an index
* @param {String} action The action to do between 'show', 'hide' and 'setActiveTab'
*/
_bufferedShowHide: function(element, action)
{
if (action == 'setActiveTab')
{
this._toShowHide['_activate_'] = element;
}
else
{
this._toShowHide[element.getId()] = action;
}
this._processShowHide();
},
/**
* Suspend the processing of awaiting show/hide/setActiveTab of ContextualLabels
* See #resumeProcessing
*/
suspendProcessing: function()
{
this.getLogger().debug("Pausing processing...")
this._pauseProcessing = true;
},
/**
* Resume the processing of awaiting show/hide/setActiveTab of ContextualLabels
* This method is asynchronous
* See #suspendProcessing
*/
resumeProcessing: function()
{
this.getLogger().debug("Resuming processing...")
this._pauseProcessing = false;
this._processShowHide();
},
/**
* @private
* Apply buffered show/hide/setActivateTab
* Beware that this function is called asynchronous since it has been created as a buffered function
*/
_processShowHide: function()
{
var me = this;
if (me._pauseProcessing)
{
this.getLogger().debug("Avoding a paused processing")
return;
}
Ext.suspendLayouts();
try
{
this.getLogger().debug("Processing")
var toActivate = me._toShowHide['_activate_'];
delete me._toShowHide['_activate_'];
var panels = {};
Ext.Object.each(me._toShowHide, function (tabElId, action) {
var tabEl = Ext.getCmp(tabElId);
tabEl[action]();
var index = me.getTabBar().items.indexOf(tabEl);
var panel = me.items.get(index);
panels[index] = panel._contextualTabGroup;
});
if (toActivate != null)
{
me.setActiveTab(toActivate);
}
me._updateLastNonContextualSpecificities();
Ext.Object.each(panels, function(index, contextualTabGroup) {
me._updateContextualGroupSpecificities(contextualTabGroup);
});
me._toShowHide = {};
}
finally
{
Ext.resumeLayouts(true);
}
},
/**
* Make the contextual tab visible
* @param {Ametys.ui.fluent.ribbon.Panel} panel The tab panel to show
* @param {Boolean} forceSelection True will ensure the tab is selected, null will be agnostic (a last selected algo will try to guess) and false will ensure it is not selected. Inherit #changeWasActiveOnHideStatus to change the logic
*/
showContextualTab: function(panel, forceSelection)
{
if (panel.contextualTab != null)
{
var index = this.items.indexOf(panel);
var tabEl = this.getTabBar().items.get(index);
if (!this._isTabVisibleNowOrSoon(tabEl))
{
this._bufferedShowHide(tabEl, 'show');
}
}
function noContextualTabForActiveTabNowOrSoon(me)
{
var t = me._getActiveTabNowOrSoon();
return t && t.contextualTab == null;
}
if (forceSelection !== false && (panel._activatedOnce == null || panel._wasActiveOnHide == true || forceSelection === true || this._lastContextualTabHidden == panel) || noContextualTabForActiveTabNowOrSoon(this))
{
panel._activatedOnce = true;
panel._wasActiveOnHide = false;
this._bufferedShowHide(panel, 'setActiveTab');
if (this.floatParent._ribbonCollapsed)
{
this.floatParent._hideCollapsed();
}
}
},
/**
* Make the contextual tab invisible
* @param {Ametys.ui.fluent.ribbon.Panel} panel The tab panel to hide
*/
hideContextualTab: function(panel)
{
this._lastContextualTabHidden = null;
this._actionPerfomed = false;
if (panel.contextualTab != null)
{
var index = this.items.indexOf(panel);
var tabEl = this.getTabBar().items.get(index);
if (this._isTabVisibleNowOrSoon(tabEl))
{
this._bufferedShowHide(tabEl, 'hide');
panel._wasActiveOnHide = this._getActiveTabNowOrSoon() == panel;
if (panel._wasActiveOnHide)
{
var index = Ext.Array.indexOf(panel._contextualTabGroup, panel);
var panelToTests = [];
for (var i = index - 1; i >= 0; i--)
{
var tab = panel._contextualTabGroup[i];
panelToTests.push(tab);
}
for (var i = index + 1; i < panel._contextualTabGroup.length; i++)
{
var tab = panel._contextualTabGroup[i];
panelToTests.push(tab);
}
for (var i = this.items.length - 1; i >= 0; i--)
{
var tab = this.items.get(i);
if (tab._contextualTabGroup != null)
{
for (var j = 0; j < tab._contextualTabGroup.length; j++)
{
var tab = tab._contextualTabGroup[j];
panelToTests.push(tab);
}
}
}
var done = false;
for (var i = 0; i < panelToTests.length; i++)
{
var tab = panelToTests[i];
var index = this.items.indexOf(tab);
var tabEl = this.getTabBar().items.get(index);
if (this._isTabVisibleNowOrSoon(tabEl))
{
this._bufferedShowHide(tab, 'setActiveTab');
done = true;
break;
}
}
if (!done)
{
this._bufferedShowHide(0, 'setActiveTab');
}
this._lastContextualTabHidden = panel;
}
this.changeWasActiveOnHideStatus(panel);
}
}
},
/**
* @private
* UI add a smaller separator between a contextual tab and the next contextual tab of the group (if both visibles)
* @param {Ametys.ui.fluent.ribbon.Panel[]} contextualGroup The contextual group to update
*/
_updateContextualGroupSpecificities: function(contextualGroup)
{
var hasAPreviousVisible = false;
for (var i = contextualGroup.length - 1; i >= 0; i--)
{
var panel = contextualGroup[i];
var index = this.items.indexOf(panel);
var tabEl = this.getTabBar().items.get(index);
var panel = contextualGroup[i];
if (hasAPreviousVisible)
{
tabEl.setMargin(this.contextualTabWithNextInSameGroupSeparation);
tabEl.addCls(this.contextualTabWithNextInSameGroupCls);
}
else
{
tabEl.setMargin(this.defaultTabSeparation);
tabEl.removeCls(this.contextualTabWithNextInSameGroupCls);
}
hasAPreviousVisible = hasAPreviousVisible || tabEl.isVisible();
}
},
/**
* @private
* UI add a separator between contextual and non-contextual, and add groups for contextual
* This function is buffered to avoid multiple calls
*/
_updateLastNonContextualSpecificities: function()
{
var lastNonContextual = this.getTabBar().items.findBy(function(item, key) {
return !item.hasCls(this.contextualTabCls) && item.nextSibling().hasCls(this.contextualTabCls)
}, this);
if (!this.getTabBar().down("button[contextual=true]:visible(true)"))
{
if (this._displaySpaceAfterLastNonContextual)
{
this._displaySpaceAfterLastNonContextual = false;
lastNonContextual.setMargin(this.defaultTabSeparation);
}
}
else
{
if (!this._displaySpaceAfterLastNonContextual)
{
this._displaySpaceAfterLastNonContextual = true;
lastNonContextual.setMargin(this.nonContextualToContextualSeparation);
}
}
},
/**
* @template
* @protected
* Implement this method to determine if panel was active when hiding.
* Default implementation rely on time ellapsed.
*/
changeWasActiveOnHideStatus: function(panel)
{
// Finally wa may want to remove this flag if activation was too recent to be true
if (panel._wasActiveOnHide && this._lastHide != null)
{
var d = new Date().getTime();
if (d - this._lastHide < 500)
{
panel._wasActiveOnHide = false;
}
}
this._lastHide = new Date().getTime();
},
/**
* @private
* Listener when mouse wheel is used over tabs
* @param {Event} e The event
*/
_onTabWheel: function(e)
{
if (this.floatParent._ribbonCollapsed)
{
return;
}
var scrollSens = (e.getWheelDelta() > 0)
var initialValue = scrollSens ? 1 : 0;
var increment = scrollSens ? -1 : 1;
var i = this.items.indexOf(this.getActiveTab());
for (var j = i + increment; j >= 0 && j < this.items.length; j += increment)
{
if (this.getTabBar().items.get(j).isVisible())
{
this.setActiveTab(this.items.get(j));
e.stopEvent();
Ext.menu.MenuMgr.hideAll();
return;
}
}
},
/**
* Enlarge or reduce groups of the active tab to best fit in the ribbon (depending on the screen width)
* @private
*/
_checkSize: function()
{
// we need to first close menu to ensure that any iconized group will go back in the ribbon prior to any scale computing
Ext.menu.MenuMgr.hideAll();
if (this.getActiveTab())
{
var currentActiveTabWidth = this.getActiveTab().getWidth();
var index = this.items.indexOf(this.getActiveTab());
if (this._scaleGrid[index] == null)
{
this._buildScalesGrid(index);
}
var grid = this._scaleGrid[index];
if (!grid.matrix[grid.current].width)
{
grid.matrix[grid.current].width = this.getActiveTab().items.last().getRegion().right - this.getActiveTab().getRegion().left;
}
this._fillGridGroupsSize(grid);
if (this.getActiveTab().items.getCount() > 0 && grid.matrix[grid.current].width > currentActiveTabWidth)
{
// does not fit, try to reduce
if (grid.current + 1 < grid.matrix.length)
{
grid.current = this._findNextIndexToTestInGrowingGrid(grid, currentActiveTabWidth);
Ext.suspendLayouts();
try
{
for (var i = 0; i < grid.matrix[grid.current].resize.length; i++)
{
this.getActiveTab().items.get(i).setScale(grid.matrix[grid.current].resize[i]);
}
}
finally
{
Ext.resumeLayouts(true);
}
// may need to go under !
this._checkSize();
}
else
{
// does not fit in the window, but already at the min size
return;
}
}
else
{
// Can we grow ?
if (grid.current > 0)
{
if (!grid.matrix[grid.current - 1].width || currentActiveTabWidth > grid.matrix[grid.current - 1].width)
{
// have a try
grid.current = this._findNextIndexToTestInDecreasingGrid(grid, currentActiveTabWidth);
Ext.suspendLayouts();
try
{
for (var i = 0; i < grid.matrix[grid.current].resize.length; i++)
{
this.getActiveTab().items.get(i).setScale(grid.matrix[grid.current].resize[i]);
}
}
finally
{
Ext.resumeLayouts(true);
}
this._checkSize();
}
}
}
}
},
/**
* @private
* Test the given grid for the largest scale possible while screen is reduced
* @param {Object} grid One grid of #_scaleGrid
* @param {Number} currentActiveTabWidth The width available
*/
_findNextIndexToTestInGrowingGrid: function(grid, currentActiveTabWidth)
{
var currentAnswer = grid.current + 1;
for (var i = currentAnswer; i < grid.matrix.length - 1; i++)
{
if (grid.matrix[i].width)
{
if (currentActiveTabWidth >= grid.matrix[i].width)
{
return i;
}
currentAnswer = i + 1;
}
}
return currentAnswer;
},
/**
* @private
* Test the given grid for the largest scale possible while screen is enlarged
* @param {Object} grid One grid of #_scaleGrid
* @param {Number} currentActiveTabWidth The width available
*/
_findNextIndexToTestInDecreasingGrid: function(grid, currentActiveTabWidth)
{
var currentAnswer = grid.current - 1;
for (var i = currentAnswer; i >= 0; i--)
{
if (grid.matrix[i].width)
{
if (currentActiveTabWidth <= grid.matrix[i].width)
{
return i + 1;
}
currentAnswer = i;
}
}
return currentAnswer;
},
/**
* Build the scale grid #_scaleGrid.
* @param {Number} index The tab index to compute
* @private
*/
_buildScalesGrid: function(index)
{
var scaleGrid = this._scaleGrid;
scaleGrid[index] = [];
var tab = this.items.get(index);
// compute the priority between groups
var priority = [];
tab.items.each(function (e, i)
{
priority.push({index: i, priority: e.priority ? e.priority : 0});
});
function compare(e1, e2)
{
return (e1.priority != e2.priority) ? e1.priority < e2.priority : e2.index < e1.index;
}
priority.sort(compare);
// fill the matrix
function fill(first, second, avoidFullFirst)
{
for (var cursor = avoidFullFirst ? 1 : 0; cursor <= priority.length; cursor++)
{
var line = [];
for (var i = 0; i < priority.length; i++) { line.push("X"); }
for (var i = 0; i < priority.length - cursor; i++)
{
line[priority[i].index] = first;
}
for (var i = 0; i < cursor; i++)
{
line[priority[priority.length - cursor + i].index] =second;
}
scaleGrid[index].matrix.push({resize: line});
}
}
scaleGrid[index].matrix = [];
scaleGrid[index].current = 0;
scaleGrid[index].groupsWidth = {};
for (var i = 0; i < priority.length; i++)
{
scaleGrid[index].groupsWidth[i] = {};
}
fill(Ametys.ui.fluent.ribbon.Group.SIZE_LARGE, Ametys.ui.fluent.ribbon.Group.SIZE_MEDIUM, false);
fill(Ametys.ui.fluent.ribbon.Group.SIZE_MEDIUM, Ametys.ui.fluent.ribbon.Group.SIZE_SMALL, true);
fill(Ametys.ui.fluent.ribbon.Group.SIZE_SMALL, Ametys.ui.fluent.ribbon.Group.SIZE_ICON, true);
// change unexisting combo & remove duplicates
for (var i = scaleGrid[index].matrix.length - 1; i >= 0 ; i--)
{
var grid = scaleGrid[index].matrix[i];
for (var j = 0; j < grid.resize.length; j++)
{
if (!tab.items.get(j).supportScale(grid.resize[j]))
{
grid.resize[j] = Ametys.ui.fluent.ribbon.Group.SIZE_MEDIUM;
}
}
if (i + 1 < scaleGrid[index].matrix.length)
{
var areTheSame = true;
var grid = scaleGrid[index].matrix[i];
var grid2 = scaleGrid[index].matrix[i + 1];
for (var j = 0; j < grid.resize.length; j++)
{
if (grid.resize[j] != grid2.resize[j])
{
areTheSame = false;
break;
}
}
if (areTheSame)
{
Ext.Array.remove(scaleGrid[index].matrix, grid2);
}
}
}
},
/**
* @private
* Fill the group size for the current tab grid, and remove any anomaly in known size group layouts
* @param {Object} tabGrid The grid for the current tab
*/
_fillGridGroupsSize: function(tabGrid)
{
// "sizeOrder" is used to know the relative order of each size compare to another
var sizeOrder = [
Ametys.ui.fluent.ribbon.Group.SIZE_LARGE,
Ametys.ui.fluent.ribbon.Group.SIZE_MEDIUM,
Ametys.ui.fluent.ribbon.Group.SIZE_SMALL,
Ametys.ui.fluent.ribbon.Group.SIZE_ICON
];
for (var groupIndex = 0; groupIndex < this.getActiveTab().items.length; groupIndex++)
{
// for each group
var currentGroup = this.getActiveTab().items.get(groupIndex);
var currentGroupSize = tabGrid.matrix[tabGrid.current].resize[groupIndex];
if (!tabGrid.groupsWidth[groupIndex][currentGroupSize])
{
// New size
tabGrid.groupsWidth[groupIndex][currentGroupSize] = currentGroup.getWidth();
var currentSizeOrder = sizeOrder.indexOf(currentGroupSize);
for (var compareToSize = 0; compareToSize < sizeOrder.length; compareToSize++)
{
if (currentSizeOrder > compareToSize && tabGrid.groupsWidth[groupIndex][sizeOrder[compareToSize]] < tabGrid.groupsWidth[groupIndex][sizeOrder[currentSizeOrder]])
{
// remove current group size, because the larger one is smaller
this._changeGroupSize(tabGrid, groupIndex, sizeOrder[currentSizeOrder], sizeOrder[compareToSize]);
}
if (currentSizeOrder < compareToSize && tabGrid.groupsWidth[groupIndex][sizeOrder[compareToSize]] > tabGrid.groupsWidth[groupIndex][sizeOrder[currentSizeOrder]])
{
// remove compareTo group size, because it is larger than the current one
this._changeGroupSize(tabGrid, groupIndex, sizeOrder[compareToSize], sizeOrder[currentSizeOrder]);
}
}
}
}
},
/**
* @private
* Replace all occurrences of the size of a group by another size, and remove duplicate entries from the tab matrix
* @param {Object} tabGrid The tab grid
* @param {Number} groupIndex The index of the group to edit
* @param {String} oldSize The old size name
* @param {String} newSize The new size name
*/
_changeGroupSize: function(tabGrid, groupIndex, oldSize, newSize)
{
for (var index = tabGrid.matrix.length - 1; index >= 0 ; index--)
{
if (tabGrid.matrix[index].resize[groupIndex] == oldSize)
{
tabGrid.matrix[index].resize[groupIndex] = newSize;
delete tabGrid.matrix[index].width;
var foundMatch = false;
for (var compareIndex = index - 1; compareIndex >= 0 && !foundMatch; compareIndex--)
{
var areTheSame = tabGrid.matrix[index].resize.length == tabGrid.matrix[compareIndex].resize.length;
for (var resizeIndex = 0; areTheSame && resizeIndex < tabGrid.matrix[index].resize.length; resizeIndex++)
{
areTheSame = tabGrid.matrix[compareIndex].resize[resizeIndex] == tabGrid.matrix[index].resize[resizeIndex];
}
if (areTheSame)
{
if (tabGrid.current == index)
{
tabGrid.current = tabGrid.current == 0 ? 0 : tabGrid.current - 1;
}
Ext.Array.removeAt(tabGrid.matrix, index);
}
}
}
}
}
}
);