]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/Toolkit.js
correctly reset value of combobox
[pve-manager.git] / www / manager6 / Toolkit.js
1 /*global IP4_match, IP4_cidr_match, IP6_match, IP6_cidr_match, IP64_match, DnsName_match, DnsName_REGEXP, IPV4_REGEXP, IPV6_REGEXP*/
2 /*global HostPort_match, HostPortBrackets_match, IP6_dotnotation_match*/
3 // ExtJS related things
4
5 PVE.Utils.toolkit = 'extjs';
6
7 // do not send '_dc' parameter
8 Ext.Ajax.disableCaching = false;
9
10 // custom Vtypes
11 Ext.apply(Ext.form.field.VTypes, {
12 IPAddress: function(v) {
13 return IP4_match.test(v);
14 },
15 IPAddressText: gettext('Example') + ': 192.168.1.1',
16 IPAddressMask: /[\d\.]/i,
17
18 IPCIDRAddress: function(v) {
19 var result = IP4_cidr_match.exec(v);
20 // limits according to JSON Schema see
21 // pve-common/src/PVE/JSONSchema.pm
22 return (result !== null && result[1] >= 8 && result[1] <= 32);
23 },
24 IPCIDRAddressText: gettext('Example') + ': 192.168.1.1/24' + "<br>" + gettext('Valid CIDR Range') + ': 8-32',
25 IPCIDRAddressMask: /[\d\.\/]/i,
26
27 IP6Address: function(v) {
28 return IP6_match.test(v);
29 },
30 IP6AddressText: gettext('Example') + ': 2001:DB8::42',
31 IP6AddressMask: /[A-Fa-f0-9:]/,
32
33 IP6CIDRAddress: function(v) {
34 var result = IP6_cidr_match.exec(v);
35 // limits according to JSON Schema see
36 // pve-common/src/PVE/JSONSchema.pm
37 return (result !== null && result[1] >= 8 && result[1] <= 120);
38 },
39 IP6CIDRAddressText: gettext('Example') + ': 2001:DB8::42/64' + "<br>" + gettext('Valid CIDR Range') + ': 8-120',
40 IP6CIDRAddressMask: /[A-Fa-f0-9:\/]/,
41
42 IP6PrefixLength: function(v) {
43 return v >= 0 && v <= 128;
44 },
45 IP6PrefixLengthText: gettext('Example') + ': X, where 0 <= X <= 128',
46 IP6PrefixLengthMask: /[0-9]/,
47
48 IP64Address: function(v) {
49 return IP64_match.test(v);
50 },
51 IP64AddressText: gettext('Example') + ': 192.168.1.1 2001:DB8::42',
52 IP64AddressMask: /[A-Fa-f0-9\.:]/,
53
54 MacAddress: function(v) {
55 return (/^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$/).test(v);
56 },
57 MacAddressMask: /[a-fA-F0-9:]/,
58 MacAddressText: gettext('Example') + ': 01:23:45:67:89:ab',
59
60 BridgeName: function(v) {
61 return (/^vmbr\d{1,4}$/).test(v);
62 },
63 BridgeNameText: gettext('Format') + ': vmbr<b>N</b>, where 0 <= <b>N</b> <= 9999',
64
65 BondName: function(v) {
66 return (/^bond\d{1,4}$/).test(v);
67 },
68 BondNameText: gettext('Format') + ': bond<b>N</b>, where 0 <= <b>N</b> <= 9999',
69
70 InterfaceName: function(v) {
71 return (/^[a-z][a-z0-9_]{1,20}$/).test(v);
72 },
73 InterfaceNameText: gettext('Format') + ': [a-z][a-z0-9_]{1,20}',
74
75
76 QemuStartDate: function(v) {
77 return (/^(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)$/).test(v);
78 },
79 QemuStartDateText: gettext('Format') + ': "now" or "2006-06-17T16:01:21" or "2006-06-17"',
80
81 StorageId: function(v) {
82 return (/^[a-z][a-z0-9\-\_\.]*[a-z0-9]$/i).test(v);
83 },
84 StorageIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '-', '_', '.'",
85
86 ConfigId: function(v) {
87 return (/^[a-z][a-z0-9\_]+$/i).test(v);
88 },
89 ConfigIdText: gettext("Allowed characters") + ": 'A-Z', 'a-z', '0-9', '_'",
90
91 HttpProxy: function(v) {
92 return (/^http:\/\/.*$/).test(v);
93 },
94 HttpProxyText: gettext('Example') + ": http://username:password&#64;host:port/",
95
96 DnsName: function(v) {
97 return DnsName_match.test(v);
98 },
99 DnsNameText: gettext('This is not a valid DNS name'),
100
101 // workaround for https://www.sencha.com/forum/showthread.php?302150
102 pveMail: function(v) {
103 return (/^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,63}$/).test(v);
104 },
105 pveMailText: gettext('Example') + ": user@example.com",
106
107 HostList: function(v) {
108 var list = v.split(/[\ \,\;]+/);
109 var i;
110 for (i = 0; i < list.length; i++) {
111 if (list[i] == "") {
112 continue;
113 }
114
115 if (!HostPort_match.test(list[i]) &&
116 !HostPortBrackets_match.test(list[i]) &&
117 !IP6_dotnotation_match.test(list[i])) {
118 return false;
119 }
120 }
121
122 return true;
123 },
124 HostListText: gettext('Not a valid list of hosts')
125 });
126
127 // ExtJs 5-6 has an issue with caching
128 // see https://www.sencha.com/forum/showthread.php?308989
129 Ext.define('PVE.UnderlayPool', {
130 override: 'Ext.dom.UnderlayPool',
131
132 checkOut: function () {
133 var cache = this.cache,
134 len = cache.length,
135 el;
136
137 // do cleanup because some of the objects might have been destroyed
138 while (len--) {
139 if (cache[len].destroyed) {
140 cache.splice(len, 1);
141 }
142 }
143 // end do cleanup
144
145 el = cache.shift();
146
147 if (!el) {
148 el = Ext.Element.create(this.elementConfig);
149 el.setVisibilityMode(2);
150 //<debug>
151 // tell the spec runner to ignore this element when checking if the dom is clean
152 el.dom.setAttribute('data-sticky', true);
153 //</debug>
154 }
155
156 return el;
157 }
158 });
159
160 // if the order of the values are not the same in originalValue and value
161 // extjs will not overwrite value, but marks the field dirty and thus
162 // the reset button will be enabled (but clicking it changes nothing)
163 // so if the arrays are not the same after resetting, we
164 // clear and set it
165 Ext.define('PVE.form.ComboBox', {
166 override: 'Ext.form.field.ComboBox',
167
168 reset: function() {
169 // copied from combobox
170 var me = this;
171 me.callParent();
172 me.applyEmptyText();
173
174 // clear and set when not the same
175 var value = me.getValue();
176 if (Ext.isArray(me.originalValue) && Ext.isArray(value) && !Ext.Array.equals(value, me.originalValue)) {
177 me.clearValue();
178 me.setValue(me.originalValue);
179 }
180 }
181 });
182
183 // should be fixed with ExtJS 6.0.2, see:
184 // https://www.sencha.com/forum/showthread.php?307244-Bug-with-datefield-in-window-with-scroll
185 Ext.define('PVE.Datepicker', {
186 override: 'Ext.picker.Date',
187 hideMode: 'visibility'
188 });
189
190 // force alert boxes to be rendered with an Error Icon
191 // since Ext.Msg is an object and not a prototype, we need to override it
192 // after the framework has been initiated
193 Ext.onReady(function() {
194 /*jslint confusion: true */
195 Ext.override(Ext.Msg, {
196 alert: function(title, message, fn, scope) {
197 if (Ext.isString(title)) {
198 var config = {
199 title: title,
200 message: message,
201 icon: this.ERROR,
202 buttons: this.OK,
203 fn: fn,
204 scope : scope,
205 minWidth: this.minWidth
206 };
207 return this.show(config);
208 }
209 }
210 });
211 /*jslint confusion: false */
212 });
213 Ext.define('Ext.ux.IFrame', {
214 extend: 'Ext.Component',
215
216 alias: 'widget.uxiframe',
217
218 loadMask: 'Loading...',
219
220 src: 'about:blank',
221
222 renderTpl: [
223 '<iframe src="{src}" id="{id}-iframeEl" data-ref="iframeEl" name="{frameName}" width="100%" height="100%" frameborder="0" allowfullscreen="true"></iframe>'
224 ],
225 childEls: ['iframeEl'],
226
227 initComponent: function () {
228 this.callParent();
229
230 this.frameName = this.frameName || this.id + '-frame';
231 },
232
233 initEvents : function() {
234 var me = this;
235 me.callParent();
236 me.iframeEl.on('load', me.onLoad, me);
237 },
238
239 initRenderData: function() {
240 return Ext.apply(this.callParent(), {
241 src: this.src,
242 frameName: this.frameName
243 });
244 },
245
246 getBody: function() {
247 var doc = this.getDoc();
248 return doc.body || doc.documentElement;
249 },
250
251 getDoc: function() {
252 try {
253 return this.getWin().document;
254 } catch (ex) {
255 return null;
256 }
257 },
258
259 getWin: function() {
260 var me = this,
261 name = me.frameName,
262 win = Ext.isIE
263 ? me.iframeEl.dom.contentWindow
264 : window.frames[name];
265 return win;
266 },
267
268 getFrame: function() {
269 var me = this;
270 return me.iframeEl.dom;
271 },
272
273 beforeDestroy: function () {
274 this.cleanupListeners(true);
275 this.callParent();
276 },
277
278 cleanupListeners: function(destroying){
279 var doc, prop;
280
281 if (this.rendered) {
282 try {
283 doc = this.getDoc();
284 if (doc) {
285 /*jslint nomen: true*/
286 Ext.get(doc).un(this._docListeners);
287 /*jslint nomen: false*/
288 if (destroying && doc.hasOwnProperty) {
289 for (prop in doc) {
290 if (doc.hasOwnProperty(prop)) {
291 delete doc[prop];
292 }
293 }
294 }
295 }
296 } catch(e) { }
297 }
298 },
299
300 onLoad: function() {
301 var me = this,
302 doc = me.getDoc(),
303 fn = me.onRelayedEvent;
304
305 if (doc) {
306 try {
307 // These events need to be relayed from the inner document (where they stop
308 // bubbling) up to the outer document. This has to be done at the DOM level so
309 // the event reaches listeners on elements like the document body. The effected
310 // mechanisms that depend on this bubbling behavior are listed to the right
311 // of the event.
312 /*jslint nomen: true*/
313 Ext.get(doc).on(
314 me._docListeners = {
315 mousedown: fn, // menu dismisal (MenuManager) and Window onMouseDown (toFront)
316 mousemove: fn, // window resize drag detection
317 mouseup: fn, // window resize termination
318 click: fn, // not sure, but just to be safe
319 dblclick: fn, // not sure again
320 scope: me
321 }
322 );
323 /*jslint nomen: false*/
324 } catch(e) {
325 // cannot do this xss
326 }
327
328 // We need to be sure we remove all our events from the iframe on unload or we're going to LEAK!
329 Ext.get(this.getWin()).on('beforeunload', me.cleanupListeners, me);
330
331 this.el.unmask();
332 this.fireEvent('load', this);
333
334 } else if (me.src) {
335
336 this.el.unmask();
337 this.fireEvent('error', this);
338 }
339
340
341 },
342
343 onRelayedEvent: function (event) {
344 // relay event from the iframe's document to the document that owns the iframe...
345
346 var iframeEl = this.iframeEl,
347
348 // Get the left-based iframe position
349 iframeXY = iframeEl.getTrueXY(),
350 originalEventXY = event.getXY(),
351
352 // Get the left-based XY position.
353 // This is because the consumer of the injected event will
354 // perform its own RTL normalization.
355 eventXY = event.getTrueXY();
356
357 // the event from the inner document has XY relative to that document's origin,
358 // so adjust it to use the origin of the iframe in the outer document:
359 event.xy = [iframeXY[0] + eventXY[0], iframeXY[1] + eventXY[1]];
360
361 event.injectEvent(iframeEl); // blame the iframe for the event...
362
363 event.xy = originalEventXY; // restore the original XY (just for safety)
364 },
365
366 load: function (src) {
367 var me = this,
368 text = me.loadMask,
369 frame = me.getFrame();
370
371 if (me.fireEvent('beforeload', me, src) !== false) {
372 if (text && me.el) {
373 me.el.mask(text);
374 }
375
376 frame.src = me.src = (src || me.src);
377 }
378 }
379 });