]> git.proxmox.com Git - proxmox-widget-toolkit.git/blobdiff - Toolkit.js
window/Edit: accept bodyPadding from config
[proxmox-widget-toolkit.git] / Toolkit.js
index b1709bf791ea0edbbc9739b641e3fb2d26db2816..c30aae1fb94e0e31857bf9bad74ac0854d27a6fe 100644 (file)
@@ -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' + "<br>" + gettext('Valid CIDR Range') + ': 8-120',
+    IP6CIDRAddressText:  gettext('Example') + ': 2001:DB8::42/64' + "<br>" + 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') + ': vmbr<b>N</b>, where 0 <= <b>N</b> <= 9999',
 
     BondName: function(v) {
@@ -71,11 +106,6 @@ Ext.apply(Ext.form.field.VTypes, {
                       gettext("Maximum characters") + ": 21" + "<br />" +
                       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;
     }
 });
 
@@ -212,6 +456,37 @@ Ext.define('Proxmox.form.field.Text', {
     },
 });
 
+// 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});
+       }
+    },
+
+    // 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
 // since Ext.Msg is an object and not a prototype, we need to override it
 // after the framework has been initiated