// ExtJS related things
-PVE.Utils.toolkit = 'extjs',
+Proxmox.Utils.toolkit = 'extjs';
// do not send '_dc' parameter
Ext.Ajax.disableCaching = false;
// custom Vtypes
Ext.apply(Ext.form.field.VTypes, {
IPAddress: function(v) {
- return IP4_match.test(v);
+ return Proxmox.Utils.IP4_match.test(v);
},
IPAddressText: gettext('Example') + ': 192.168.1.1',
IPAddressMask: /[\d\.]/i,
IPCIDRAddress: function(v) {
- var result = IP4_cidr_match.exec(v);
+ var result = Proxmox.Utils.IP4_cidr_match.exec(v);
// limits according to JSON Schema see
// pve-common/src/PVE/JSONSchema.pm
return (result !== null && result[1] >= 8 && result[1] <= 32);
IPCIDRAddressMask: /[\d\.\/]/i,
IP6Address: function(v) {
- return IP6_match.test(v);
+ return Proxmox.Utils.IP6_match.test(v);
},
IP6AddressText: gettext('Example') + ': 2001:DB8::42',
IP6AddressMask: /[A-Fa-f0-9:]/,
IP6CIDRAddress: function(v) {
- var result = IP6_cidr_match.exec(v);
+ 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);
IP6PrefixLengthMask: /[0-9]/,
IP64Address: function(v) {
- return IP64_match.test(v);
+ return Proxmox.Utils.IP64_match.test(v);
},
IP64AddressText: gettext('Example') + ': 192.168.1.1 2001:DB8::42',
IP64AddressMask: /[A-Fa-f0-9\.:]/,
MacAddressMask: /[a-fA-F0-9:]/,
MacAddressText: gettext('Example') + ': 01:23:45:67:89:ab',
+ MacPrefix: function(v) {
+ return (/^[a-f0-9]{2}(?::[a-f0-9]{2}){0,2}:?$/i).test(v);
+ },
+ MacPrefixMask: /[a-fA-F0-9:]/,
+ MacPrefixText: gettext('Example') + ': 02:8f',
+
BridgeName: function(v) {
return (/^vmbr\d{1,4}$/).test(v);
},
InterfaceName: function(v) {
return (/^[a-z][a-z0-9_]{1,20}$/).test(v);
},
- InterfaceNameText: gettext('Format') + ': [a-z][a-z0-9_]{1,20}',
-
+ InterfaceNameText: gettext("Allowed characters") + ": 'a-z', '0-9', '_'" + "<br />" +
+ gettext("Minimum characters") + ": 2" + "<br />" +
+ 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);
StorageId: function(v) {
return (/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i).test(v);
},
- StorageIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '-', '_', '.'",
+ StorageIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '-', '_', '.'" + "<br />" +
+ gettext("Minimum characters") + ": 2" + "<br />" +
+ gettext("Must start with") + ": 'A-Z', 'a-z'<br />" +
+ gettext("Must end with") + ": 'A-Z', 'a-z', '0-9'<br />",
ConfigId: function(v) {
return (/^[a-z][a-z0-9\_]+$/i).test(v);
},
- ConfigIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '_'",
+ ConfigIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '_'" + "<br />" +
+ gettext("Minimum characters") + ": 2" + "<br />" +
+ gettext("Must start with") + ": " + gettext("letter"),
HttpProxy: function(v) {
return (/^http:\/\/.*$/).test(v);
HttpProxyText: gettext('Example') + ": http://username:password@host:port/",
DnsName: function(v) {
- return (/^(([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)\.)*([A-Za-z0-9]([A-Za-z0-9\-]*[A-Za-z0-9])?)$/).test(v);
+ return Proxmox.Utils.DnsName_match.test(v);
},
DnsNameText: gettext('This is not a valid DNS name'),
return (/^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,63}$/).test(v);
},
pveMailText: gettext('Example') + ": user@example.com",
+
+ HostList: function(v) {
+ var list = v.split(/[\ \,\;]+/);
+ var i;
+ for (i = 0; i < list.length; i++) {
+ if (list[i] == "") {
+ continue;
+ }
+
+ if (!Proxmox.Utils.HostPort_match.test(list[i]) &&
+ !Proxmox.Utils.HostPortBrackets_match.test(list[i]) &&
+ !Proxmox.Utils.IP6_dotnotation_match.test(list[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+ HostListText: gettext('Not a valid list of hosts')
+});
+
+// since 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
}
});
+// '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)
+// so if the arrays are not the same after resetting, we
+// clear and set it
+Ext.define('PVE.form.ComboBox', {
+ override: 'Ext.form.field.ComboBox',
+
+ reset: function() {
+ // copied from combobox
+ var me = this;
+ me.callParent();
+
+ // clear and set when not the same
+ var value = me.getValue();
+ if (Ext.isArray(me.originalValue) && Ext.isArray(value) && !Ext.Array.equals(value, me.originalValue)) {
+ me.clearValue();
+ me.setValue(me.originalValue);
+ }
+ }
+});
+
+// when refreshing the view of a grid/tree
+// the restoring of the focus brings the
+// focused item back in the view, even when we scrolled away
+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;
+ }
+});
+
// should be fixed with ExtJS 6.0.2, see:
// https://www.sencha.com/forum/showthread.php?307244-Bug-with-datefield-in-window-with-scroll
Ext.define('PVE.Datepicker', {
hideMode: 'visibility'
});
+// this should be fixed with ExtJS 6.0.2
+// this makes mousescrolling work in firefox in the overflowhandler
+// and does not change behaviour in any other browser
+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
+ // nearly the same as the default onMouseWheel handler,
+ // but using deltaY instead of wheelDeltaY
+ // and no normalizing, because it is already normalized
+ onMouseWheelFirefox: function(e) {
+ e.stopEvent();
+ var delta = e.browserEvent.deltaY || 0;
+ this.scrollBy(delta * this.wheelIncrement, false);
+ }
+
+});
+
// 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
try {
doc = this.getDoc();
if (doc) {
+ /*jslint nomen: true*/
Ext.get(doc).un(this._docListeners);
- if (destroying) {
+ /*jslint nomen: false*/
+ if (destroying && doc.hasOwnProperty) {
for (prop in doc) {
- if (doc.hasOwnProperty && doc.hasOwnProperty(prop)) {
+ if (doc.hasOwnProperty(prop)) {
delete doc[prop];
}
}
// the event reaches listeners on elements like the document body. The effected
// mechanisms that depend on this bubbling behavior are listed to the right
// of the event.
+ /*jslint nomen: true*/
Ext.get(doc).on(
me._docListeners = {
mousedown: fn, // menu dismisal (MenuManager) and Window onMouseDown (toFront)
scope: me
}
);
+ /*jslint nomen: false*/
} catch(e) {
// cannot do this xss
}