X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=Toolkit.js;h=c30aae1fb94e0e31857bf9bad74ac0854d27a6fe;hb=271171f81afe93f88393bd5857e3d7d578747075;hp=811bbca77d2da10c2fb7f7282d47fd0f6f494646;hpb=36704a2fcc0d995d20f70f386f883c00cb4fc859;p=proxmox-widget-toolkit.git diff --git a/Toolkit.js b/Toolkit.js index 811bbca..c30aae1 100644 --- a/Toolkit.js +++ b/Toolkit.js @@ -3,6 +3,11 @@ // do not send '_dc' parameter Ext.Ajax.disableCaching = false; +// FIXME: HACK! Makes scrolling in number spinner work again. fixed in ExtJS >= 6.1 +if (Ext.isFirefox) { + Ext.$eventNameMap.DOMMouseScroll = 'DOMMouseScroll'; +} + // custom Vtypes Ext.apply(Ext.form.field.VTypes, { IPAddress: function(v) { @@ -30,9 +35,9 @@ Ext.apply(Ext.form.field.VTypes, { var result = Proxmox.Utils.IP6_cidr_match.exec(v); // limits according to JSON Schema see // pve-common/src/PVE/JSONSchema.pm - return (result !== null && result[1] >= 8 && result[1] <= 120); + return (result !== null && result[1] >= 8 && result[1] <= 128); }, - IP6CIDRAddressText: gettext('Example') + ': 2001:DB8::42/64' + "
" + gettext('Valid CIDR Range') + ': 8-120', + IP6CIDRAddressText: gettext('Example') + ': 2001:DB8::42/64' + "
" + gettext('Valid CIDR Range') + ': 8-128', IP6CIDRAddressMask: /[A-Fa-f0-9:\/]/, IP6PrefixLength: function(v) { @@ -47,15 +52,45 @@ Ext.apply(Ext.form.field.VTypes, { IP64AddressText: gettext('Example') + ': 192.168.1.1 2001:DB8::42', IP64AddressMask: /[A-Fa-f0-9\.:]/, + IP64CIDRAddress: function(v) { + var result = Proxmox.Utils.IP64_cidr_match.exec(v); + if (result === null) { + return false; + } + if (result[1] !== undefined) { + return result[1] >= 8 && result[1] <= 128; + } else if (result[2] !== undefined) { + return result[2] >= 8 && result[2] <= 32; + } else { + return false; + } + }, + IP64CIDRAddressText: gettext('Example') + ': 192.168.1.1/24 2001:DB8::42/64', + IP64CIDRAddressMask: /[A-Fa-f0-9\.:\/]/, + MacAddress: function(v) { return (/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/).test(v); }, MacAddressMask: /[a-fA-F0-9:]/, MacAddressText: gettext('Example') + ': 01:23:45:67:89:ab', + MacPrefix: function(v) { + return (/^[a-f0-9][02468ace](?::[a-f0-9]{2}){0,2}:?$/i).test(v); + }, + MacPrefixMask: /[a-fA-F0-9:]/, + MacPrefixText: gettext('Example') + ': 02:8f - ' + gettext('only unicast addresses are allowed'), + BridgeName: function(v) { return (/^vmbr\d{1,4}$/).test(v); }, + VlanName: function(v) { + if (Proxmox.Utils.VlanInterface_match.test(v)) { + return true; + } else if (Proxmox.Utils.Vlan_match.test(v)) { + return true; + } + return true; + }, BridgeNameText: gettext('Format') + ': vmbrN, where 0 <= N <= 9999', BondName: function(v) { @@ -71,11 +106,6 @@ Ext.apply(Ext.form.field.VTypes, { gettext("Maximum characters") + ": 21" + "
" + gettext("Must start with") + ": 'a-z'", - QemuStartDate: function(v) { - return (/^(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)$/).test(v); - }, - QemuStartDateText: gettext('Format') + ': "now" or "2006-06-17T16:01:21" or "2006-06-17"', - StorageId: function(v) { return (/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i).test(v); }, @@ -107,6 +137,16 @@ Ext.apply(Ext.form.field.VTypes, { }, proxmoxMailText: gettext('Example') + ": user@example.com", + DnsOrIp: function(v) { + if (!Proxmox.Utils.DnsName_match.test(v) && + !Proxmox.Utils.IP64_match.test(v)) { + return false; + } + + return true; + }, + DnsOrIpText: gettext('Not a valid DNS name or IP address.'), + HostList: function(v) { var list = v.split(/[\ \,\;]+/); var i; @@ -138,6 +178,95 @@ Ext.apply(Ext.form.field.VTypes, { passwordText: gettext('Passwords do not match') }); +// Firefox 52+ Touchscreen bug +// see https://www.sencha.com/forum/showthread.php?336762-Examples-don-t-work-in-Firefox-52-touchscreen/page2 +// and https://bugzilla.proxmox.com/show_bug.cgi?id=1223 +Ext.define('EXTJS_23846.Element', { + override: 'Ext.dom.Element' +}, function(Element) { + var supports = Ext.supports, + proto = Element.prototype, + eventMap = proto.eventMap, + additiveEvents = proto.additiveEvents; + + if (Ext.os.is.Desktop && supports.TouchEvents && !supports.PointerEvents) { + eventMap.touchstart = 'mousedown'; + eventMap.touchmove = 'mousemove'; + eventMap.touchend = 'mouseup'; + eventMap.touchcancel = 'mouseup'; + + additiveEvents.mousedown = 'mousedown'; + additiveEvents.mousemove = 'mousemove'; + additiveEvents.mouseup = 'mouseup'; + additiveEvents.touchstart = 'touchstart'; + additiveEvents.touchmove = 'touchmove'; + additiveEvents.touchend = 'touchend'; + additiveEvents.touchcancel = 'touchcancel'; + + additiveEvents.pointerdown = 'mousedown'; + additiveEvents.pointermove = 'mousemove'; + additiveEvents.pointerup = 'mouseup'; + additiveEvents.pointercancel = 'mouseup'; + } +}); + +Ext.define('EXTJS_23846.Gesture', { + override: 'Ext.event.publisher.Gesture' +}, function(Gesture) { + var me = Gesture.instance; + + if (Ext.supports.TouchEvents && !Ext.isWebKit && Ext.os.is.Desktop) { + me.handledDomEvents.push('mousedown', 'mousemove', 'mouseup'); + me.registerEvents(); + } +}); + +Ext.define('EXTJS_18900.Pie', { + override: 'Ext.chart.series.Pie', + + // from 6.0.2 + betweenAngle: function (x, a, b) { + var pp = Math.PI * 2, + offset = this.rotationOffset; + + if (a === b) { + return false; + } + + if (!this.getClockwise()) { + x *= -1; + a *= -1; + b *= -1; + a -= offset; + b -= offset; + } else { + a += offset; + b += offset; + } + + x -= a; + b -= a; + + // Normalize, so that both x and b are in the [0,360) interval. + x %= pp; + b %= pp; + x += pp; + b += pp; + x %= pp; + b %= pp; + + // Because 360 * n angles will be normalized to 0, + // we need to treat b === 0 as a special case. + return x < b || b === 0; + }, +}); + +// we always want the number in x.y format and never in, e.g., x,y +Ext.define('PVE.form.field.Number', { + override: 'Ext.form.field.Number', + submitLocaleSeparator: false +}); + // ExtJs 5-6 has an issue with caching // see https://www.sencha.com/forum/showthread.php?308989 Ext.define('Proxmox.UnderlayPool', { @@ -171,6 +300,20 @@ Ext.define('Proxmox.UnderlayPool', { } }); +// 'Enter' in Textareas and aria multiline fields should not activate the +// defaultbutton, fixed in extjs 6.0.2 +Ext.define('PVE.panel.Panel', { + override: 'Ext.panel.Panel', + + fireDefaultButton: function(e) { + if (e.target.getAttribute('aria-multiline') === 'true' || + e.target.tagName === "TEXTAREA") { + return true; + } + return this.callParent(arguments); + } +}); + // if the order of the values are not the same in originalValue and value // extjs will not overwrite value, but marks the field dirty and thus // the reset button will be enabled (but clicking it changes nothing) @@ -183,7 +326,6 @@ Ext.define('Proxmox.form.ComboBox', { // copied from combobox var me = this; me.callParent(); - me.applyEmptyText(); // clear and set when not the same var value = me.getValue(); @@ -191,6 +333,108 @@ Ext.define('Proxmox.form.ComboBox', { me.clearValue(); me.setValue(me.originalValue); } + }, + + // we also want to open the trigger on editable comboboxes by default + initComponent: function() { + let me = this; + me.callParent(); + + if (me.editable) { + // The trigger.picker causes first a focus event on the field then + // toggles the selection picker. Thus skip expanding in this case, + // else our focus listener expands and the picker.trigger then + // collapses it directly afterwards. + Ext.override(me.triggers.picker, { + onMouseDown: function(e) { + // copied "should we focus" check from Ext.form.trigger.Trigger + if (e.pointerType !== 'touch' && !this.field.owns(Ext.Element.getActiveElement())) { + me.skip_expand_on_focus = true; + } + this.callParent(arguments); + } + }); + + me.on("focus", function(combobox) { + if (!combobox.isExpanded && !combobox.skip_expand_on_focus) { + combobox.expand(); + } + combobox.skip_expand_on_focus = false; + }); + } + }, +}); + +// when refreshing a grid/tree view, restoring the focus moves the view back to +// the previously focused item. Save scroll position before refocusing. +Ext.define(null, { + override: 'Ext.view.Table', + + jumpToFocus: false, + + saveFocusState: function() { + var me = this, + store = me.dataSource, + actionableMode = me.actionableMode, + navModel = me.getNavigationModel(), + focusPosition = actionableMode ? me.actionPosition : navModel.getPosition(true), + refocusRow, refocusCol; + + if (focusPosition) { + // Separate this from the instance that the nav model is using. + focusPosition = focusPosition.clone(); + + // Exit actionable mode. + // We must inform any Actionables that they must relinquish control. + // Tabbability must be reset. + if (actionableMode) { + me.ownerGrid.setActionableMode(false); + } + + // Blur the focused descendant, but do not trigger focusLeave. + me.el.dom.focus(); + + // Exiting actionable mode navigates to the owning cell, so in either focus mode we must + // clear the navigation position + navModel.setPosition(); + + // The following function will attempt to refocus back in the same mode to the same cell + // as it was at before based upon the previous record (if it's still inthe store), or the row index. + return function() { + // If we still have data, attempt to refocus in the same mode. + if (store.getCount()) { + + // Adjust expectations of where we are able to refocus according to what kind of destruction + // might have been wrought on this view's DOM during focus save. + refocusRow = Math.min(focusPosition.rowIdx, me.all.getCount() - 1); + refocusCol = Math.min(focusPosition.colIdx, me.getVisibleColumnManager().getColumns().length - 1); + focusPosition = new Ext.grid.CellContext(me).setPosition( + store.contains(focusPosition.record) ? focusPosition.record : refocusRow, refocusCol); + + if (actionableMode) { + me.ownerGrid.setActionableMode(true, focusPosition); + } else { + me.cellFocused = true; + + // we sometimes want to scroll back to where we were + var x = me.getScrollX(); + var y = me.getScrollY(); + + // Pass "preventNavigation" as true so that that does not cause selection. + navModel.setPosition(focusPosition, null, null, null, true); + + if (!me.jumpToFocus) { + me.scrollTo(x,y); + } + } + } + // No rows - focus associated column header + else { + focusPosition.column.focus(); + } + }; + } + return Ext.emptyFn; } }); @@ -201,17 +445,46 @@ Ext.define('Proxmox.Datepicker', { hideMode: 'visibility' }); -// ExtJS 6.0.1 has no get/setSubmitValue() (although you find it in the docs). +// ExtJS 6.0.1 has no setSubmitValue() (although you find it in the docs). +// Note: this.submitValue is a boolean flag, whereas getSubmitValue() returns +// data to be submitted. Ext.define('Proxmox.form.field.Text', { override: 'Ext.form.field.Text', setSubmitValue: function(v) { this.submitValue = v; }, +}); + +// this should be fixed with ExtJS 6.0.2 +// make mousescrolling work in firefox in the containers overflowhandler +Ext.define(null, { + override: 'Ext.layout.container.boxOverflow.Scroller', + + createWheelListener: function() { + var me = this; + if (Ext.isFirefox) { + me.wheelListener = me.layout.innerCt.on('wheel', me.onMouseWheelFirefox, me, {destroyable: true}); + } else { + me.wheelListener = me.layout.innerCt.on('mousewheel', me.onMouseWheel, me, {destroyable: true}); + } + }, - getSubmitValue: function(v) { - return this.submitValue; + // special wheel handler for firefox. differs from the default onMouseWheel + // handler by using deltaY instead of wheelDeltaY and no normalizing, + // because it is already + onMouseWheelFirefox: function(e) { + e.stopEvent(); + var delta = e.browserEvent.deltaY || 0; + this.scrollBy(delta * this.wheelIncrement, false); } + +}); + +// add '@' to the valid id +Ext.define('Proxmox.validIdReOverride', { + override: 'Ext.Component', + validIdRe: /^[a-z_][a-z0-9\-_\@]*$/i, }); // force alert boxes to be rendered with an Error Icon