From bb64de6e768299414c4a027aebcf4b5c1fb34d9a Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Tue, 31 Jan 2017 17:32:18 +0100 Subject: [PATCH] add Toolkit.js Copied from pve-manager --- Makefile | 1 + Toolkit.js | 382 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 383 insertions(+) create mode 100644 Toolkit.js diff --git a/Makefile b/Makefile index d2d60d5..3675e0e 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ WWWBASEDIR=${DESTDIR}/usr/share/javascript/${PACKAGE} JSSRC= \ Utils.js \ + Toolkit.js \ data/reader/JsonObject.js \ data/ProxmoxProxy.js \ data/UpdateQueue.js \ diff --git a/Toolkit.js b/Toolkit.js new file mode 100644 index 0000000..277276d --- /dev/null +++ b/Toolkit.js @@ -0,0 +1,382 @@ +// ExtJS related things + + // do not send '_dc' parameter +Ext.Ajax.disableCaching = false; + +// custom Vtypes +Ext.apply(Ext.form.field.VTypes, { + IPAddress: function(v) { + return Proxmox.Utils.IP4_match.test(v); + }, + IPAddressText: gettext('Example') + ': 192.168.1.1', + IPAddressMask: /[\d\.]/i, + + IPCIDRAddress: function(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); + }, + IPCIDRAddressText: gettext('Example') + ': 192.168.1.1/24' + "
" + gettext('Valid CIDR Range') + ': 8-32', + IPCIDRAddressMask: /[\d\.\/]/i, + + IP6Address: function(v) { + return Proxmox.Utils.IP6_match.test(v); + }, + IP6AddressText: gettext('Example') + ': 2001:DB8::42', + IP6AddressMask: /[A-Fa-f0-9:]/, + + IP6CIDRAddress: function(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); + }, + IP6CIDRAddressText: gettext('Example') + ': 2001:DB8::42/64' + "
" + gettext('Valid CIDR Range') + ': 8-120', + IP6CIDRAddressMask: /[A-Fa-f0-9:\/]/, + + IP6PrefixLength: function(v) { + return v >= 0 && v <= 128; + }, + IP6PrefixLengthText: gettext('Example') + ': X, where 0 <= X <= 128', + IP6PrefixLengthMask: /[0-9]/, + + IP64Address: function(v) { + return Proxmox.Utils.IP64_match.test(v); + }, + IP64AddressText: gettext('Example') + ': 192.168.1.1 2001:DB8::42', + IP64AddressMask: /[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', + + BridgeName: function(v) { + return (/^vmbr\d{1,4}$/).test(v); + }, + BridgeNameText: gettext('Format') + ': vmbrN, where 0 <= N <= 9999', + + BondName: function(v) { + return (/^bond\d{1,4}$/).test(v); + }, + BondNameText: gettext('Format') + ': bondN, where 0 <= N <= 9999', + + InterfaceName: function(v) { + return (/^[a-z][a-z0-9_]{1,20}$/).test(v); + }, + InterfaceNameText: gettext("Allowed characters") + ": 'a-z', '0-9', '_'" + "
" + + gettext("Minimum characters") + ": 2" + "
" + + 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); + }, + StorageIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '-', '_', '.'" + "
" + + gettext("Minimum characters") + ": 2" + "
" + + gettext("Must start with") + ": 'A-Z', 'a-z'
" + + gettext("Must end with") + ": 'A-Z', 'a-z', '0-9'
", + + ConfigId: function(v) { + return (/^[a-z][a-z0-9\_]+$/i).test(v); + }, + ConfigIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '_'" + "
" + + gettext("Minimum characters") + ": 2" + "
" + + 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 Proxmox.Utils.DnsName_match.test(v); + }, + DnsNameText: gettext('This is not a valid DNS name'), + + // workaround for https://www.sencha.com/forum/showthread.php?302150 + proxmoxMail: function(v) { + return (/^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,63}$/).test(v); + }, + proxmoxMailText: 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') +}); + +// ExtJs 5-6 has an issue with caching +// see https://www.sencha.com/forum/showthread.php?308989 +Ext.define('Proxmox.UnderlayPool', { + override: 'Ext.dom.UnderlayPool', + + checkOut: function () { + var cache = this.cache, + len = cache.length, + el; + + // do cleanup because some of the objects might have been destroyed + while (len--) { + if (cache[len].destroyed) { + cache.splice(len, 1); + } + } + // end do cleanup + + el = cache.shift(); + + if (!el) { + el = Ext.Element.create(this.elementConfig); + el.setVisibilityMode(2); + // + // tell the spec runner to ignore this element when checking if the dom is clean + el.dom.setAttribute('data-sticky', true); + // + } + + return el; + } +}); + +// 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('Proxmox.form.ComboBox', { + override: 'Ext.form.field.ComboBox', + + reset: function() { + // copied from combobox + var me = this; + me.callParent(); + me.applyEmptyText(); + + // 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); + } + } +}); + +// 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('Proxmox.Datepicker', { + override: 'Ext.picker.Date', + hideMode: 'visibility' +}); + +// 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 +Ext.onReady(function() { +/*jslint confusion: true */ + Ext.override(Ext.Msg, { + alert: function(title, message, fn, scope) { + if (Ext.isString(title)) { + var config = { + title: title, + message: message, + icon: this.ERROR, + buttons: this.OK, + fn: fn, + scope : scope, + minWidth: this.minWidth + }; + return this.show(config); + } + } + }); +/*jslint confusion: false */ +}); +Ext.define('Ext.ux.IFrame', { + extend: 'Ext.Component', + + alias: 'widget.uxiframe', + + loadMask: 'Loading...', + + src: 'about:blank', + + renderTpl: [ + '' + ], + childEls: ['iframeEl'], + + initComponent: function () { + this.callParent(); + + this.frameName = this.frameName || this.id + '-frame'; + }, + + initEvents : function() { + var me = this; + me.callParent(); + me.iframeEl.on('load', me.onLoad, me); + }, + + initRenderData: function() { + return Ext.apply(this.callParent(), { + src: this.src, + frameName: this.frameName + }); + }, + + getBody: function() { + var doc = this.getDoc(); + return doc.body || doc.documentElement; + }, + + getDoc: function() { + try { + return this.getWin().document; + } catch (ex) { + return null; + } + }, + + getWin: function() { + var me = this, + name = me.frameName, + win = Ext.isIE + ? me.iframeEl.dom.contentWindow + : window.frames[name]; + return win; + }, + + getFrame: function() { + var me = this; + return me.iframeEl.dom; + }, + + beforeDestroy: function () { + this.cleanupListeners(true); + this.callParent(); + }, + + cleanupListeners: function(destroying){ + var doc, prop; + + if (this.rendered) { + try { + doc = this.getDoc(); + if (doc) { + /*jslint nomen: true*/ + Ext.get(doc).un(this._docListeners); + /*jslint nomen: false*/ + if (destroying && doc.hasOwnProperty) { + for (prop in doc) { + if (doc.hasOwnProperty(prop)) { + delete doc[prop]; + } + } + } + } + } catch(e) { } + } + }, + + onLoad: function() { + var me = this, + doc = me.getDoc(), + fn = me.onRelayedEvent; + + if (doc) { + try { + // These events need to be relayed from the inner document (where they stop + // bubbling) up to the outer document. This has to be done at the DOM level so + // 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) + mousemove: fn, // window resize drag detection + mouseup: fn, // window resize termination + click: fn, // not sure, but just to be safe + dblclick: fn, // not sure again + scope: me + } + ); + /*jslint nomen: false*/ + } catch(e) { + // cannot do this xss + } + + // We need to be sure we remove all our events from the iframe on unload or we're going to LEAK! + Ext.get(this.getWin()).on('beforeunload', me.cleanupListeners, me); + + this.el.unmask(); + this.fireEvent('load', this); + + } else if (me.src) { + + this.el.unmask(); + this.fireEvent('error', this); + } + + + }, + + onRelayedEvent: function (event) { + // relay event from the iframe's document to the document that owns the iframe... + + var iframeEl = this.iframeEl, + + // Get the left-based iframe position + iframeXY = iframeEl.getTrueXY(), + originalEventXY = event.getXY(), + + // Get the left-based XY position. + // This is because the consumer of the injected event will + // perform its own RTL normalization. + eventXY = event.getTrueXY(); + + // the event from the inner document has XY relative to that document's origin, + // so adjust it to use the origin of the iframe in the outer document: + event.xy = [iframeXY[0] + eventXY[0], iframeXY[1] + eventXY[1]]; + + event.injectEvent(iframeEl); // blame the iframe for the event... + + event.xy = originalEventXY; // restore the original XY (just for safety) + }, + + load: function (src) { + var me = this, + text = me.loadMask, + frame = me.getFrame(); + + if (me.fireEvent('beforeload', me, src) !== false) { + if (text && me.el) { + me.el.mask(text); + } + + frame.src = me.src = (src || me.src); + } + } +}); -- 2.39.2