/*
* Copyright 2017 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.
*/
// ------------------------------
// Here are ExtJS bug fixes
// ------------------------------
(function()
{
Ext.override(Ext.layout.ContextItem, {
// Fix CMS-5908 http://www.sencha.com/forum/showthread.php?291412-Error-after-upgrade-to-ExtJS-4.2.3
init: function (full, options) {
var me = this;
var protection = false;
if (me.ownerLayout && !target.ownerLayout.isItemBoxParent)
{
target.ownerLayout.isItemBoxParent = function() { return false; };
protection = true;
}
var returnValue = this.callParent(arguments);
if (protection)
{
delete target.ownerLayout.isItemBoxParent;
}
return returnValue;
}
});
Ext.override(Ext.menu.Menu, {
initComponent: function()
{
this.callParent(arguments);
this.on('resize', this._onResize, this);
},
// Fix for CMS-5997 http://www.sencha.com/forum/showthread.php?297558-ExtJs-4.2.3-Adding-items-to-an-opened-menu-on-a-floating-parent&p=1086597#post1086597
_onResize: function(menu, width, height, oldWidth, oldHeight, eOpts)
{
if (this.isVisible() /*&& oldHeight != null && oldWidth != null */&& this.floatParent && !this._doingShowBy)
{
this._doingShowBy = true; // let's avoid infinite showBy -> needResize -> showBy -> ...
this.showBy(this.ownerCmp, this.ownerCmp.menuAlign);
this._doingShowBy = false;
}
}
});
Ext.override(Ext.view.DropZone, {
// Fix for CMS-6262 https://www.sencha.com/forum/showthread.php?301552-ExtJS-4.2.3-Drag-n-drop-in-a-grid-and-invalid-zone.&p=1101961#post1101961
containsRecordAtOffset: function(records, record, offset)
{
if (!record) {
return false;
}
var view = this.view,
recordIndex = view.indexOf(record),
nodeBefore = view.getNode(recordIndex + offset, true),
recordBefore = nodeBefore ? view.getRecord(nodeBefore) : null;
var containsRecordAtOffset = recordBefore && Ext.Array.contains(records, recordBefore);
if (!containsRecordAtOffset)
{
return false;
}
else if (record.store.getGroupField() != null && Ext.Array.findBy(this.view.features, function(item) { return item.ftype == "grouping" }) != null)
{
// using groups, we need to ignore items from different groups
var groups = [];
for (var i = 0; i < records.length; i++)
{
groups.push(records[i].get(record.store.getGroupField()));
}
var targetGroup = record.get(record.store.getGroupField());
return Ext.Array.contains(groups, targetGroup);
}
else
{
return true;
}
}
});
// Fix for CMS-6366 https://www.sencha.com/forum/showthread.php?304867-D-n-D-over-an-IFrame-issue
if (Ext.ux && Ext.ux.IFrame)
{
Ext.override(Ext.dd.DragDropManager, {
/**
* @private
* @member Ext.dd.DragDropManager
* @method _onAll
* @ametys
* @since Ametys Runtime 4.1
* Execute action on all that need to be protected
* @param {Function} action The action to do
* @param {Ext.Component} action.component The component to act on
*/
_onAll: function(action)
{
Ext.ComponentManager.each(function(key, component) {
if (component.isXType('uxiframe') || component.isXType('richtextfield'))
{
action(component);
}
});
},
handleMouseDown: function(e, oDD)
{
this.callParent(arguments);
// Find all iframes to "protect them"
this._onAll(function (component) {
component = component.bodyEl ? /* richtext or code, need to apply on underlying element, if not a UI issue will appear */ component.bodyEl : /* iframe */component;
component.addCls("iframe-protected")
component.mask();
});
},
handleMouseUp: function(e)
{
this.callParent(arguments);
// Find all iframes to "unprotect them"
this._onAll(function (component) {
component = component.bodyEl ? /* richtext or code, need to apply on underlying element, if not a UI issue will appear */ component.bodyEl : /* iframe */component;
component.removeCls("iframe-protected")
component.unmask();
});
}
});
Ext.override(Ext.ux.IFrame, {
// Fix for RUNTIME-3116
// Loading url while iframe is not rendered yet
load: function(src)
{
var me = this;
if (me.rendered)
{
me.callParent(arguments);
}
else
{
// We will load when the rendering will be done
var args = Array.from(arguments);
me.on({'render': { fn: function() { me.load.call(me, args); }, scope: me, single: true }});
}
}
});
}
Ext.override(Ext.data.Model, {
privates: {
statics: {
// Fix for https://issues.ametys.org/browse/CMS-6363
// Actually, this enables to specify a convert or calculate function for an id field in a Ext.data.Model (which does not work, is it a bug ?)
// See https://www.sencha.com/forum/showthread.php?292044-Ext.data.Field.convert%28%29-not-called-for-idField-if-only-calculated
initFields: function (data, cls, proto) {
var me = this,
idField;
me.callParent(arguments);
idField = proto.idField;
idField.defaultValue = (idField.convert) ? undefined : null; // defaultValue must be undefined instead of null if a convert function is specified
}
}
},
inheritableStatics: {
// Fix for https://issues.ametys.org/browse/CMS-8330
// When updating model and reconfiguring columns, the new columns sometimes did not have their value set (cache on fieldExtractors not cleared)
replaceFields: function(newFields, removeFields) {
var me = this;
me.callParent(arguments);
if (me.fieldExtractors)
{
delete me.fieldExtractors[me.getProxy().getReader().$className];
}
}
}
});
Ext.override(Ext.form.field.Base, {
// Fix for https://issues.ametys.org/browse/RUNTIME-1858
// See https://www.sencha.com/forum/showthread.php?311209-Autocomplete-with-Chrome&p=1136279#post1136279
getSubTplMarkup: function(fieldData)
{
var value = this.callParent(arguments);
if (Ext.isChrome && fieldData.$comp && fieldData.$comp.inputType == 'password')
{
value = value.replace('autocomplete="off"', 'autocomplete="new-password"');
}
return value;
}
});
Ext.override(Ext.form.field.Date, {
// Fix for https://issues.ametys.org/browse/RUNTIME-2658
formatText: null
});
Ext.override(Ext.form.field.Time, {
// Fix for https://issues.ametys.org/browse/RUNTIME-2658
formatText: null
});
Ext.override(Ext.form.field.ComboBox, {
// Fix for https://issues.ametys.org/browse/CMS-5934
// https://www.sencha.com/forum/showthread.php?339854-ExtJS-6-2-Filtering-a-combox-tagfield-and-backspace&p=1179339#post1179339
// Also fix for https://issues.ametys.org/browse/CMS-8760 [Widget] Typing a comma in the select-referencetable-content widget
// as we still want to search the entire user input if it contains the delimiter
/**
* @private
* @member Ext.form.field.ComboBox
* @property {String} _lastRawValue The last raw input value
* @since Ametys Runtime 4.0
* @ametys
*/
_lastRawValue: null,
doRawQuery: function() {
var me = this,
rawValue = me.inputEl.dom.value;
// Use final bit after comma as query value if multiselecting (Ametys edit: no !!)
// if (me.multiSelect) {
// rawValue = rawValue.split(me.delimiter).pop();
// }
// Here is the fix
if (Ext.isString(me._lastRawValue) && Ext.isString(rawValue) && rawValue.length < me.minChars && me.minChars <= me._lastRawValue.length)
{
// last value is longer than current, so the user removed some characters in the query (by pressing BACKSPACE for instance...)
// and current value is shorter than the threshold (me.minChars)
// and last value is equal or greater than the threshold
// So force to query in order to always have the same results with the same inputs
me.doQuery("", true, true);
}
else
{
me.doQuery(rawValue, false, true);
}
me._lastRawValue = rawValue;
}
});
// Fix for RUNTIME-2575 https://www.sencha.com/forum/showthread.php?454691-In-Ext-data-Store-the-remove-method-is-not-ok-with-doc
Ext.override(Ext.data.Store, {
remove: function(records, isMove, silent) {
if (Ext.isNumber(records))
{
records = [records];
}
return this.callParent(arguments);
}
});
// Fix for CTREE-19 https://www.sencha.com/forum/showthread.php?366515-Untranslated-labels-in-ExtJS-6-5-1&p=1210058#post1210058
Ext.override(Ext.tree.plugin.TreeViewDragDrop, {
dragText: "{{i18n PLUGINS_CORE_UI_TREEDRAGNDROP_LABEL}}"
});
// Fix for RUNTIME-1640 https://www.sencha.com/forum/showthread.php?366515-Untranslated-labels-in-ExtJS-6-5-1&p=1210058#post1210058
Ext.override(Ext.panel.Panel, {
collapseToolText: "{{i18n PLUGINS_CORE_UI_PANEL_COLLAPSE}}",
expandToolText: "{{i18n PLUGINS_CORE_UI_PANEL_EXPAND}}"
});
// Fix for CMS-8635
Ext.override(Ext.tree.Panel, {
ensureVisible: function()
{
if (this.getView().getNodeContainer())
{
this.callParent(arguments);
}
else
{
this.getLogger().warn("Avoid a UI crash by discarding Ext.tree.Panel#ensureVisible on a semi rendered tree view");
}
}
});
// Fix for CMS-8958
Ext.override(Ext.view.BoundList, {
onEndUpdate: function() {
var me = this;
if (me.updateSuspendCounter) {
--me.updateSuspendCounter;
Ext.resumeLayouts(true); // The fix is putting this line inside the if instead of after
}
else
{
this.getLogger().warn("resuming an unsuspended layout");
}
if (me.refreshSizePending) {
me.refreshSize(true);
me.refreshSizePending = false;
}
}
});
// Fix for FRONTEDIT-100 (already fixed in extjs 6.5.3)
Ext.override(Ext.util.Positionable, {
getAlignToRegion: function(alignToEl, posSpec, offset, minHeight) {
var me = this,
inside,
newRegion;
alignToEl = Ext.fly(alignToEl.el || alignToEl);
if (!alignToEl || !alignToEl.dom) {
//<debug>
Ext.raise({
sourceClass: 'Ext.util.Positionable',
sourceMethod: 'getAlignToXY',
msg: 'Attempted to align an element that doesn\'t exist'
});
//</debug>
}
posSpec = me.convertPositionSpec(posSpec);
// If position spec ended with a "?" or "!", then constraining is necessary
if (posSpec.constrain) {
// Constrain to the correct enclosing object:
// If the assertive form was used (like "tl-bl!"), constrain to the alignToEl.
if (posSpec.constrain === '!') {
inside = alignToEl;
}
else {
// Otherwise, attempt to use the constrainTo property.
// Otherwise, if we are a Component, there will be a container property.
// Otherwise, use this Positionable's element's parent node.
inside = me.constrainTo || me.container || me.el.parent();
}
inside = Ext.fly(inside.el || inside).getConstrainRegion();
}
// Back from extjs 6.5.3
if (alignToEl === Ext.getBody()) {
bodyScroll = alignToEl.getScroll();
offset = [bodyScroll.left, bodyScroll.top];
}
newRegion = me.getRegion().alignTo({
target: alignToEl.getRegion(),
inside: inside,
minHeight: minHeight,
offset: offset,
align: posSpec,
axisLock: true
});
return newRegion;
}
});
Ext.override(Ext.window.Window, {
getAlignToRegion: function(alignToEl, posSpec, offset, minHeight) {
var me = this,
inside,
newRegion;
alignToEl = Ext.fly(alignToEl.el || alignToEl);
if (!alignToEl || !alignToEl.dom) {
//<debug>
Ext.raise({
sourceClass: 'Ext.util.Positionable',
sourceMethod: 'getAlignToXY',
msg: 'Attempted to align an element that doesn\'t exist'
});
//</debug>
}
posSpec = me.convertPositionSpec(posSpec);
// If position spec ended with a "?" or "!", then constraining is necessary
if (posSpec.constrain) {
// Constrain to the correct enclosing object:
// If the assertive form was used (like "tl-bl!"), constrain to the alignToEl.
if (posSpec.constrain === '!') {
inside = alignToEl;
}
else {
// Otherwise, attempt to use the constrainTo property.
// Otherwise, if we are a Component, there will be a container property.
// Otherwise, use this Positionable's element's parent node.
inside = me.constrainTo || me.container || me.el.parent();
}
inside = Ext.fly(inside.el || inside).getConstrainRegion();
}
// Back from extjs 6.5.3
if (alignToEl === Ext.getBody()) {
bodyScroll = alignToEl.getScroll();
offset = [bodyScroll.left, bodyScroll.top];
}
newRegion = me.getRegion().alignTo({
target: alignToEl.getRegion(),
inside: inside,
minHeight: minHeight,
offset: offset,
align: posSpec,
axisLock: true
});
return newRegion;
}
});
// Fix for RUNTIME-3119
Ext.override(Ext.Component, {
mask: function()
{
if (this.rendered)
{
this.callParent(arguments);
}
else
{
this._shouldBeMasked = Ext.Array.from(arguments);
}
},
unmask: function()
{
if (this.rendered)
{
this.callParent(arguments);
}
else
{
this._shouldBeMasked = null;
}
},
afterRender: function()
{
this.callParent(arguments);
if (this._shouldBeMasked)
{
this.mask.call(this, this._shouldBeMasked);
}
},
destroy: function()
{
this._shouldBeMasked = null;
this.callParent(arguments);
}
});
/**
* @member Ext.app.ViewController
* @method afterRender
* After render
*/
/**
* @member Ext.util.Floating
* @event tofront
* When bring to front
*/
/**
* @member Ext.data.proxy.Server
* @event beginprocessresponse
* When starting to process answer
* @param {Object} response The response
* @param {Object} operation The running operation
*/
/**
* @member Ext.data.proxy.Server
* @event endprocessresponse
* When starting to process answer
* @param {Object} response The response
* @param {Object} operation The running operation
*/
/**
* @member Ext.panel.Table
* @event viewcreated
* When a table view was created
* @param {Ext.panel.Table} panel The view owner
* @param {Ext.view.Table} view The view
*/
/**
* @member Ext.panel.Panel
* @event beginfloat
* When a collasped panel is starting to float
* @param {Ext.panel.Panel} panel The panel
*/
/**
* @member Ext.panel.Panel
* @event endfloat
* When a collasped panel is stopping to float
* @param {Ext.panel.Panel} panel The panel
*/
})();