/*
* Copyright 2013 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 stands for the logic of a tab of the ribbon that handle show/hide:
* * depending of the current selection
* * depending of the currently focused tool
* If both are supplyed, a OR is operated.
*/
Ext.define(
"Ametys.ribbon.element.tab.TabController",
{
extend: "Ametys.ribbon.element.RibbonTabController",
/**
* @cfg {String} selection-target-id Specify this configuration to obtain a tab that show/hide depending on the current selection type. The string is a regexp that have to match the current selection type. A leading '!' will reverse the regexp condition. See #cfg-subtarget-id.
*/
/**
* @cfg {Object} selection-target-parameter Use this configuration in addition to #cfg-selection-target-id in order to be more specific. This allow to check a target parameter.
* @cfg {String} selection-target-parameter.name The name of the parameter to check. The string is a regexp that have to match the current selection type. A leading '!' will reverse the regexp condition.
* @cfg {String} selection-target-parameter.value The value of the parameter to check. The string is a regexp that have to match the current selection type. A leading '!' will reverse the regexp condition. If the parameter is an array, it will check if the value is part of the array (using Ext.Array.contains)
*/
/**
* @cfg {String} selection-subtarget-id When specified as the same time as #cfg-selection-target-id is, the tab will be show/hide only if the selection target is matching #cfg-selection-target-id AND if there is a subtarget that matched this regexp. A leading '!' will reverse the regexp condition. See #cfg-subtarget-id.
*/
/**
* @cfg {Object} selection-subtarget-parameter Same as #cfg-selection-target-parameter but applying to #cfg-selection-subtarget-id
*/
/**
* @cfg {String} selection-subsubtarget-id Same as #cfg-subtarget-id at a third level.
*/
/**
* @cfg {Object} selection-subsubtarget-parameter Same as #cfg-selection-target-parameter but applying to #cfg-selection-subsubtarget-id
*/
/**
* @cfg {String} selection-subsubsubtarget-id Same as #cfg-subtarget-id at a fourth level.
*/
/**
* @cfg {Object} selection-subsubsubtarget-parameter Same as #cfg-selection-target-parameter but applying to #cfg-selection-subsubsubtarget-id
*/
/**
* @cfg {Boolean/String} only-first-level-target Specify to true to restrict the depth for filtering the selection target to the first level only. Otherwise it will search in all subtargets.
*/
/**
* @property {Boolean} _selection See #cfg-selection-target-id. True means the tab takes care of the selection
* @private
*/
/**
* @property {Ametys.message.MessageTarget[]} _matchingTargets The array of currently selected target matching the desired target type. See {@ link#cfg-selection-target-id}.
* @private
*/
/**
* @property {RegExp} _selectionTargetId See #cfg-selection-target-id converted as a regexp. The leading '!' is transmitted to {@link #_reversedSelectionTargetId}
* @private
*/
/**
* @property {Boolean} _reversedSelectionTargetId The leading '!' from {@link #cfg-selection-target-id} converted to true.
* @private
*/
/**
* @property {RegExp} _selectionSubtargetId See #cfg-selection-subtarget-id converted as a regexp. The leading '!' is transmitted to #_selectionReversedSubtargetId
* @private
*/
/**
* @property {Boolean} _selectionReversedSubtargetId The leading '!' from #cfg-subtarget-id converted to true.
* @private
*/
/**
* @property {RegExp} _selectionSubsubtargetId See #cfg-selection-subsubtarget-id converted as a regexp. The leading '!' is transmitted to #_selectionReversedSubsubtargetId
* @private
*/
/**
* @property {Boolean} _selectionReversedSubsubtargetId The leading '!' from #cfg-subsubtarget-id converted to true.
* @private
*/
/**
* @property {RegExp} _selectionSubsubsubtargetId See #cfg-selection-subsubsubtarget-id converted as a regexp. The leading '!' is transmitted to #_selectionReversedSubsubsubtargetId
* @private
*/
/**
* @property {Boolean} _selectionReversedSubsubsubtargetId The leading '!' from #cfg-subsubsubtarget-id converted to true.
* @private
*/
/**
* @property {Boolean} _onlyFirstLevelTarget The internal property corresponding to #cfg-only-first-level-target
* @private
*/
/**
* @cfg {String} tool-id When specified, the tab will only be visible if a tool with this id is focused. This is a regexp. A leading '!' will reverse the regexp condition.
*/
/**
* @cfg {String} tool-property When specified, the button will only be enabled if a tool with this (non-false) property is focused/activated or opened. This is the name of a property. Can be a comma-separated list of properties that must all exists.
*/
/**
* @property {RegExp} _toolId The #cfg-tool-id converted as a regexp. The '!' condition is available in #_toolReveredRole.
* @private
*/
/**
* @property {Boolean} _toolReveredRole The #cfg-tool-id converted as a regexp and this boolean stands for the '!' condition.
* @private
*/
/**
* @property {String[]} _toolProperties The #cfg-tool-property
* @private
*/
/**
* @property {Boolean} _toolFocused When using #cfg-tool-id/#cfg-tool-property this boolean reflects the focus state of the associated tool.
*/
constructor: function(config)
{
this.callParent(arguments);
var targetId = this.getInitialConfig("selection-target-id") || this.getInitialConfig("target-id");
this._matchingTargets = [];
if (targetId)
{
this._selection = true;
Ametys.message.MessageBus.on(Ametys.message.Message.SELECTION_CHANGED, this._onSelectionChanged, this);
this._onlyFirstLevelTarget = String(this.getInitialConfig("only-first-level-target")) == "true";
var i = targetId.indexOf('!');
if (i == 0)
{
this._selectionTargetId = new RegExp(targetId.substring(1));
this._reversedSelectionTargetId = true;
}
else
{
this._selectionTargetId = new RegExp(targetId);
this._reversedSelectionTargetId = false;
}
// Has an associated target-parameter check?
var targetParameter = this.getInitialConfig("selection-target-parameter");
if (targetParameter)
{
var i = targetParameter.name.indexOf('!');
if (i == 0)
{
this._selectionTargetParameterName = new RegExp(targetParameter.name.substring(1));
this._reversedSelectionTargetParameterName = true;
}
else
{
this._selectionTargetParameterName = new RegExp(targetParameter.name);
this._reversedSelectionTargetParameterName = false;
}
i = targetParameter.value.indexOf('!');
if (i == 0)
{
this._selectionTargetParameterValue = new RegExp(targetParameter.value.substring(1));
this._reversedSelectionTargetParameterValue = true;
}
else
{
this._selectionTargetParameterValue = new RegExp(targetParameter.value);
this._reversedSelectionTargetParameterValue = false;
}
}
var subtargetId = this.getInitialConfig("selection-subtarget-id") || this.getInitialConfig("subtarget-id");
if (subtargetId)
{
var i = subtargetId.indexOf('!');
if (i == 0)
{
this._selectionSubtargetId = new RegExp(subtargetId.substring(1));
this._selectionReversedSubtargetId = true;
}
else
{
this._selectionSubtargetId = new RegExp(subtargetId);
this._selectionReversedSubtargetId = false;
}
// Has an associated subtarget-parameter check?
var subtargetParameter = this.getInitialConfig("selection-subtarget-parameter");
if (subtargetParameter)
{
var i = subtargetParameter.name.indexOf('!');
if (i == 0)
{
this._selectionSubtargetParameterName = new RegExp(subtargetParameter.name.substring(1));
this._reversedSelectionSubTargetParameterName = true;
}
else
{
this._selectionSubtargetParameterName = new RegExp(subtargetParameter.name);
this._reversedSelectionSubTargetParameterName = false;
}
i = subtargetParameter.value.indexOf('!');
if (i == 0)
{
this._selectionSubtargetParameterValue = new RegExp(subtargetParameter.value.substring(1));
this._reversedSelectionSubTargetParameterValue = true;
}
else
{
this._selectionSubtargetParameterValue = new RegExp(subtargetParameter.value);
this._reversedSelectionSubTargetParameterValue = false;
}
}
var subsubtargetId = this.getInitialConfig("selection-subsubtarget-id") || this.getInitialConfig("subsubtarget-id");
if (subsubtargetId)
{
var i = subsubtargetId.indexOf('!');
if (i == 0)
{
this._selectionSubsubtargetId = new RegExp(subsubtargetId.substring(1));
this._selectionReversedSubsubtargetId = true;
}
else
{
this._selectionSubsubtargetId = new RegExp(subsubtargetId);
this._selectionReversedSubsubtargetId = false;
}
// Has an associated subsubtarget-parameter check?
var subsubtargetParameter = this.getInitialConfig("selection-subsubtarget-parameter");
if (subsubtargetParameter)
{
var i = subsubtargetParameter.name.indexOf('!');
if (i == 0)
{
this._selectionSubsubTargetParameterName = new RegExp(subsubtargetParameter.name.substring(1));
this._reversedSelectionSubsubTargetParameterName = true;
}
else
{
this._selectionSubsubTargetParameterName = new RegExp(subsubtargetParameter.name);
this._reversedSelectionSubsubTargetParameterName = false;
}
i = subsubtargetParameter.value.indexOf('!');
if (i == 0)
{
this._selectionSubsubTargetParameterValue = new RegExp(subsubtargetParameter.value.substring(1));
this._reversedSelectionSubsubTargetParameterValue = true;
}
else
{
this._selectionSubsubTargetParameterValue = new RegExp(subsubtargetParameter.value);
this._reversedSelectionSubsubTargetParameterValue = false;
}
}
var subsubsubtargetId = this.getInitialConfig("selection-subsubsubtarget-id") || this.getInitialConfig("subsubsubtarget-id");
if (subsubsubtargetId)
{
var i = subsubsubtargetId.indexOf('!');
if (i == 0)
{
this._selectionSubsubsubtargetId = new RegExp(subsubsubtargetId.substring(1));
this._selectionReversedSubsubsubtargetId = true;
}
else
{
this._selectionSubsubsubtargetId = new RegExp(subsubsubtargetId);
this._selectionReversedSubsubsubtargetId = false;
}
// Has an associated subsubsubtarget-parameter check?
var subsubsubtargetParameter = this.getInitialConfig("selection-subsubsubtarget-parameter");
if (subsubsubtargetParameter)
{
var i = subsubsubtargetParameter.name.indexOf('!');
if (i == 0)
{
this._selectionSubsubsubTargetParameterName = new RegExp(subsubsubtargetParameter.name.substring(1));
this._reversedSelectionSubsubsubTargetParameterName = true;
}
else
{
this._selectionSubsubsubTargetParameterName = new RegExp(subsubsubtargetParameter.name);
this._reversedSelectionSubsubsubTargetParameterName = false;
}
i = subsubsubtargetParameter.value.indexOf('!');
if (i == 0)
{
this._selectionSubsubsubTargetParameterValue = new RegExp(subsubsubtargetParameter.value.substring(1));
this._reversedSelectionSubsubsubTargetParameterValue = true;
}
else
{
this._selectionSubsubsubTargetParameterValue = new RegExp(subsubsubtargetParameter.value);
this._reversedSelectionSubsubsubTargetParameterValue = false;
}
}
}
}
}
}
var toolId = this.getInitialConfig("tool-id");
var toolProperty = this.getInitialConfig("tool-property");
if (toolId || toolProperty)
{
this._toolFocused = false;
Ametys.message.MessageBus.on(Ametys.message.Message.TOOL_FOCUSED, this._onAnyToolFocused, this);
Ametys.message.MessageBus.on(Ametys.message.Message.TOOL_BLURRED, this._onAnyToolBlurred, this);
if (toolId)
{
var i = toolId.indexOf('!');
if (i == 0)
{
this._toolId = new RegExp(toolId.substring(1));
this._toolReveredRole = true;
}
else
{
this._toolId = new RegExp(toolId);
this._toolReveredRole = false;
}
}
if (toolProperty)
{
Ametys.message.MessageBus.on(Ametys.message.Message.TOOL_PARAMS_UPDATED, this._onParamsUpdated, this);
this._toolProperties = toolProperty.split(',');
}
}
},
/**
* Listener when the selection has changed. Registered only if #cfg-selection-target-id is specified, but can always be called manually.
* Will show or hide the tab effectively upon the current selection.
* @param {Ametys.message.Message} [message] The selection message. Can be null to get the last selection message
* @protected
*/
_onSelectionChanged: function(message)
{
message = message || Ametys.message.MessageBus.getCurrentSelectionMessage();
this._matchingTargets = this._getMatchingSelectionTargets(message);
if (this._toolFocused === true)
{
// this configured tool is already focused, nothing to do with selection
return;
}
if (this._selection)
{
var noSelection = message.getTargets().length == 0;
if (noSelection || this._matchingTargets.length == 0)
{
this.hide();
}
else
{
var forceSelection = null;
if (message.getParameters() && message.getParameters()["creation"])
{
// we cannot inline this 'if' instruction as null and false are not the same for forceSelection
forceSelection = (message.getParameters()["creation"] === this._matchingTargets[0].getId());
}
this.show(forceSelection);
}
}
},
/**
* Listener when a tool has been focused. Registered only if #cfg-tool-id is specified. Will enable the buttons effectively.
* @param {Ametys.message.Message} message The focus message
* @protected
*/
_onAnyToolFocused: function(message)
{
if (this._getMatchingToolsTarget(message).length > 0)
{
this._toolFocused = true;
this.show();
}
},
/**
* Listener when a tool has been blurred. Registered only if #cfg-tool-id is specified. Will disable the buttons effectively.
* @param {Ametys.message.Message} message The focus message
* @protected
*/
_onAnyToolBlurred: function(message)
{
if (this._getMatchingToolsTarget(message).length > 0)
{
this._toolFocused = false;
// even if the tool is blurred, the selection may still matching
if (!this._selection || this._matchingTargets.length == 0)
{
this.hide();
}
}
},
/**
* Listener when a tool params have been updated. Registered only if #cfg-tool-id is specified. Show or hide tab effectively.
* @param {Ametys.message.Message} message The updated message
* @protected
*/
_onParamsUpdated: function(message)
{
var matchingTools = this._getMatchingToolsTarget(message);
if (matchingTools.length > 0 && matchingTools[0].getParameters().tool.hasFocus())
{
this.show();
}
else
{
if (this._selection && this._matchingTargets.length > 0)
{
// even if the tool is not parametrized, the selection is still matching
return;
}
this.hide();
}
},
/**
* Get the matching targets in the message
* Test if the message if matching upon the #_selectionTargetId, #_selectionSubtargetId and #_selectionSubsubtargetId and #_selectionSubsubsubtargetId.
* It also checks for #_selectionTargetParameter, #_selectionSubtargetParameter, #_selectionSubsubTargetParameter and #_selectionSubsubsubTargetParameter
* @param {Ametys.message.Message} message The message to test
* @return {Ametys.message.MessageTarget[]} The non-null array of matching targets
* @private
*/
_getMatchingSelectionTargets: function(message)
{
var me = this;
var finalTargets = [];
if (this._selection)
{
var targets = message.getTargets(Ext.bind(this._testTargetLevel0, this, [this._selectionTargetId, this._reversedSelectionTargetId, this._selectionTargetParameterName, this._reversedSelectionTargetParameterName, this._selectionTargetParameterValue, this._reversedSelectionTargetParameterValue], true), this._onlyFirstLevelTarget ? 1 : 0);
if (!me._selectionSubtargetId)
{
finalTargets = targets;
}
else
{
for (var i = 0; i < targets.length; i++)
{
var stargets = targets[i].getSubtargets(Ext.bind(this._testTargetLevel1, this, [this._selectionSubtargetId, this._selectionReversedSubtargetId, this._selectionSubtargetParameterName, this._reversedSelectionSubTargetParameterName, this._selectionSubtargetParameterValue, this._reversedSelectionSubTargetParameterValue], true), 1);
if (!me._selectionSubsubtargetId)
{
if (stargets.length > 0 || (me._selectionReversedSubtargetId && targets[i].getSubtargets().length == 0))
{
finalTargets.push(targets[i]);
}
}
else
{
for (var j = 0; j < stargets.length; j++)
{
var sstargets = stargets[j].getSubtargets(Ext.bind(this._testTargetLevel2, this, [this._selectionSubsubtargetId, this._selectionReversedSubsubtargetId, this._selectionSubsubTargetParameterName, this._reversedSelectionSubsubTargetParameterName, this._selectionSubsubTargetParameterValue, this._reversedSelectionSubsubTargetParameterValue], true), 1);
if (!me._selectionSubsubsubtargetId)
{
if (sstargets.length > 0 || (me._selectionReversedSubsubtargetId && stargets[j].getSubtargets().length == 0))
{
finalTargets.push(targets[i]);
}
}
else
{
for (var k = 0; k < sstargets.length; k++)
{
var ssstargets = sstargets[k].getSubtargets(Ext.bind(this._testTargetLevel3, this, [this._selectionSubsubsubtargetId, this._selectionReversedSubsubsubtargetId, this._selectionSubsubsubTargetParameterName, this._reversedSelectionSubsubsubTargetParameterName, this._selectionSubsubsubTargetParameterValue, this._reversedSelectionSubsubsubTargetParameterValue], true), 1);
if (ssstargets.length > 0)
{
finalTargets.push(targets[i]);
}
}
}
}
}
}
}
}
return finalTargets;
},
/**
* @private
* Tests if the target of level 0 matches the configured #cfg-selection-target-id
* @return true if the target matches
*/
_testTargetLevel0: function (target)
{
return this._testTarget(target, this._selectionTargetId, this._reversedSelectionTargetId, this._selectionTargetParameterName, this._reversedSelectionTargetParameterName, this._selectionTargetParameterValue, this._reversedSelectionTargetParameterValue);
},
/**
* @private
* Tests if the target of level 1 matches the configured #cfg-selection-subtarget-id
* @return true if the target matches
*/
_testTargetLevel1: function (target)
{
return this._testTarget(target, this._selectionSubtargetId, this._selectionReversedSubtargetId, this._selectionSubtargetParameterName, this._reversedSelectionSubTargetParameterName, this._selectionSubtargetParameterValue, this._reversedSelectionSubTargetParameterValue);
},
/**
* @private
* Tests if the target of level 2 matches the configured #cfg-selection-subsubtarget-id
* @return true if the target matches
*/
_testTargetLevel2: function (target)
{
return this._testTarget(target, this._selectionSubsubtargetId, this._selectionReversedSubsubtargetId, this._selectionSubsubTargetParameterName, this._reversedSelectionSubsubTargetParameterName, this._selectionSubsubTargetParameterValue, this._reversedSelectionSubsubTargetParameterValue);
},
/**
* @private
* Tests if the target of level 3 matches the configured #cfg-selection-subsubsubtarget-id
* @return true if the target matches
*/
_testTargetLevel3: function (target)
{
return this._testTarget(target, this._selectionSubsubsubtargetId, this._selectionReversedSubsubsubtargetId, this._selectionSubsubsubTargetParameterName, this._reversedSelectionSubsubsubTargetParameterName, this._selectionSubsubsubTargetParameterValue, this._reversedSelectionSubsubsubTargetParameterValue);
},
/**
* @private
* Tests if the target of level 0 matches the configured #cfg-selection-target-id
* @param {Ametys.message.MessageTarget} target The target to test
* @param {RegExp} selectionTypeRegexp The regular expression to pass on targetId
* @param {Boolean} selectionTypeReverseRegexp True is the preceding regular expression test shoul be reversed
* @param {RegExp} selectionParameterNameRegexp If non-empty, this will test all target's parameters name and a least one must match, with a correct value (see following parameters)
* @param {Boolean} selectionParameterNameReverseRegexp True is the preceding regular expression test shoul be reversed
* @param {RegExp} selectionParameterValueRegexp Used when a parameter name is matching to test its value.
* @param {Boolean} selectionParameterValueReverseRegexp True is the preceding regular expression test shoul be reversed
* @return true if the target matches
*/
_testTarget: function (target, selectionTypeRegexp, selectionTypeReverseRegexp, selectionParameterNameRegexp, selectionParameterNameReverseRegexp, selectionParameterValueRegexp, selectionParameterValueReverseRegexp)
{
function checkParameters()
{
if (!selectionParameterNameRegexp)
{
return true;
}
function checkValue(value)
{
if (Ext.isArray(value))
{
var gotOne = false;
Ext.each(value, function(v, index, array) {
if (checkValue(v))
{
gotOne = true;
return false; // stop the iteration
}
});
return gotOne;
}
else
{
return ((!selectionParameterValueReverseRegexp && selectionParameterValueRegexp.test(value)
|| selectionParameterValueReverseRegexp && !selectionParameterValueRegexp.test(value)));
}
}
var gotOne = false;
Ext.Object.each(target.getParameters(), function(key, value, parameters) {
if ((!selectionParameterNameReverseRegexp && selectionParameterNameRegexp.test(key)
|| selectionParameterNameReverseRegexp && !selectionParameterNameRegexp.test(key))
&& value != null && checkValue(value))
{
gotOne = true;
return false; // stop the iteration
}
});
return gotOne;
}
return (!selectionTypeReverseRegexp && selectionTypeRegexp.test(target.getId())
|| selectionTypeReverseRegexp && !selectionTypeRegexp.test(target.getId()))
&& checkParameters();
},
/**
* Get the matching targets in the message
* Test if the message if matching upon the #_toolId
* @param {Ametys.message.Message} message The message to test
* @returns {Ametys.message.MessageTarget[]} The non-null array of matching targets
* @private
*/
_getMatchingToolsTarget: function(message)
{
var me = this;
function _hasAllProperties(tool)
{
for (var s = 0; s < me._toolProperties.length; s++)
{
var toolProperty = me._toolProperties[s].trim();
var reverseToolProperty = toolProperty.indexOf('!') == 0;
if (reverseToolProperty)
{
toolProperty = toolProperty.substring(1);
}
if ((!reverseToolProperty && !tool[toolProperty]) || (reverseToolProperty && tool[toolProperty]))
{
return false;
}
}
return true;
}
if (this._toolId || this._toolProperties)
{
return message.getTargets(
function (target)
{
return (!me._toolId || (!me._toolReveredRole && me._toolId.test(target.getParameters()['id'])
|| me._toolReveredRole && !me._toolId.test(target.getParameters()['id'])))
&& (!me._toolProperties || _hasAllProperties(target.getParameters()['tool']));
}
);
}
else
{
return [];
}
}
}
);