2 Ext
.ns('Proxmox.Setup');
4 if (!Ext
.isDefined(Proxmox
.Setup
.auth_cookie_name
)) {
5 throw "Proxmox library not initialized";
8 // avoid errors related to Accessible Rich Internet Applications
9 // (access for people with disabilities)
10 // TODO reenable after all components are upgraded
11 Ext
.enableAria
= false;
12 Ext
.enableAriaButtons
= false;
13 Ext
.enableAriaPanels
= false;
15 // avoid errors when running without development tools
16 if (!Ext
.isDefined(Ext
.global
.console
)) {
28 Ext
.global
.console
= console
;
31 Ext
.Ajax
.defaultHeaders
= {
32 'Accept': 'application/json',
35 Ext
.Ajax
.on('beforerequest', function(conn
, options
) {
36 if (Proxmox
.CSRFPreventionToken
) {
37 if (!options
.headers
) {
40 options
.headers
.CSRFPreventionToken
= Proxmox
.CSRFPreventionToken
;
42 let storedAuth
= Proxmox
.Utils
.getStoredAuth();
43 if (storedAuth
.token
) {
44 options
.headers
.Authorization
= storedAuth
.token
;
48 Ext
.define('Proxmox.Utils', { // a singleton
51 yesText
: gettext('Yes'),
52 noText
: gettext('No'),
53 enabledText
: gettext('Enabled'),
54 disabledText
: gettext('Disabled'),
55 noneText
: gettext('none'),
56 NoneText
: gettext('None'),
57 errorText
: gettext('Error'),
58 unknownText
: gettext('Unknown'),
59 defaultText
: gettext('Default'),
60 daysText
: gettext('days'),
61 dayText
: gettext('day'),
62 runningText
: gettext('running'),
63 stoppedText
: gettext('stopped'),
64 neverText
: gettext('never'),
65 totalText
: gettext('Total'),
66 usedText
: gettext('Used'),
67 directoryText
: gettext('Directory'),
68 stateText
: gettext('State'),
69 groupText
: gettext('Group'),
74 zh_CN
: 'Chinese (Simplified)',
75 zh_TW
: 'Chinese (Traditional)',
79 eu
: 'Euskera (Basque)',
85 nb
: 'Norwegian (Bokmal)',
86 nn
: 'Norwegian (Nynorsk)',
87 fa
: 'Persian (Farsi)',
89 pt_BR
: 'Portuguese (Brazil)',
97 render_language: function(value
) {
99 return Proxmox
.Utils
.defaultText
+ ' (English)';
101 let text
= Proxmox
.Utils
.language_map
[value
];
103 return text
+ ' (' + value
+ ')';
108 language_array: function() {
109 let data
= [['__default__', Proxmox
.Utils
.render_language('')]];
110 Ext
.Object
.each(Proxmox
.Utils
.language_map
, function(key
, value
) {
111 data
.push([key
, Proxmox
.Utils
.render_language(value
)]);
117 bond_mode_gettext_map
: {
118 '802.3ad': 'LACP (802.3ad)',
119 'lacp-balance-slb': 'LACP (balance-slb)',
120 'lacp-balance-tcp': 'LACP (balance-tcp)',
123 render_bond_mode
: value
=> Proxmox
.Utils
.bond_mode_gettext_map
[value
] || value
|| '',
125 bond_mode_array: function(modes
) {
126 return modes
.map(mode
=> [mode
, Proxmox
.Utils
.render_bond_mode(mode
)]);
129 getNoSubKeyHtml: function(url
) {
130 // url http://www.proxmox.com/products/proxmox-ve/subscription-service-plans
131 return Ext
.String
.format('You do not have a valid subscription for this server. Please visit <a target="_blank" href="{0}">www.proxmox.com</a> to get a list of available options.', url
|| 'https://www.proxmox.com');
134 format_boolean_with_default: function(value
) {
135 if (Ext
.isDefined(value
) && value
!== '__default__') {
136 return value
? Proxmox
.Utils
.yesText
: Proxmox
.Utils
.noText
;
138 return Proxmox
.Utils
.defaultText
;
141 format_boolean: function(value
) {
142 return value
? Proxmox
.Utils
.yesText
: Proxmox
.Utils
.noText
;
145 format_neg_boolean: function(value
) {
146 return !value
? Proxmox
.Utils
.yesText
: Proxmox
.Utils
.noText
;
149 format_enabled_toggle: function(value
) {
150 return value
? Proxmox
.Utils
.enabledText
: Proxmox
.Utils
.disabledText
;
153 format_expire: function(date
) {
155 return Proxmox
.Utils
.neverText
;
157 return Ext
.Date
.format(date
, "Y-m-d");
160 // somewhat like a human would tell durations, omit zero values and do not
161 // give seconds precision if we talk days already
162 format_duration_human: function(ut
) {
163 let seconds
= 0, minutes
= 0, hours
= 0, days
= 0;
170 seconds
= Number((remaining
% 60).toFixed(1));
171 remaining
= Math
.trunc(remaining
/ 60);
173 minutes
= remaining
% 60;
174 remaining
= Math
.trunc(remaining
/ 60);
176 hours
= remaining
% 24;
177 remaining
= Math
.trunc(remaining
/ 24);
185 let add
= (t
, unit
) => {
186 if (t
> 0) res
.push(t
+ unit
);
190 let addSeconds
= !add(days
, 'd');
196 return res
.join(' ');
199 format_duration_long: function(ut
) {
200 let days
= Math
.floor(ut
/ 86400);
202 let hours
= Math
.floor(ut
/ 3600);
204 let mins
= Math
.floor(ut
/ 60);
207 let hours_str
= '00' + hours
.toString();
208 hours_str
= hours_str
.substr(hours_str
.length
- 2);
209 let mins_str
= "00" + mins
.toString();
210 mins_str
= mins_str
.substr(mins_str
.length
- 2);
211 let ut_str
= "00" + ut
.toString();
212 ut_str
= ut_str
.substr(ut_str
.length
- 2);
215 let ds
= days
> 1 ? Proxmox
.Utils
.daysText
: Proxmox
.Utils
.dayText
;
216 return days
.toString() + ' ' + ds
+ ' ' +
217 hours_str
+ ':' + mins_str
+ ':' + ut_str
;
219 return hours_str
+ ':' + mins_str
+ ':' + ut_str
;
223 format_subscription_level: function(level
) {
226 } else if (level
=== 'b') {
228 } else if (level
=== 's') {
230 } else if (level
=== 'p') {
233 return Proxmox
.Utils
.noneText
;
237 compute_min_label_width: function(text
, width
) {
238 if (width
=== undefined) { width
= 100; }
240 let tm
= new Ext
.util
.TextMetrics();
241 let min
= tm
.getWidth(text
+ ':');
243 return min
< width
? width
: min
;
246 getStoredAuth: function() {
247 let storedAuth
= JSON
.parse(window
.localStorage
.getItem('ProxmoxUser'));
248 return storedAuth
|| {};
251 setAuthData: function(data
) {
252 Proxmox
.UserName
= data
.username
;
253 Proxmox
.LoggedOut
= data
.LoggedOut
;
254 // creates a session cookie (expire = null)
255 // that way the cookie gets deleted after the browser window is closed
257 Proxmox
.CSRFPreventionToken
= data
.CSRFPreventionToken
;
258 Ext
.util
.Cookies
.set(Proxmox
.Setup
.auth_cookie_name
, data
.ticket
, null, '/', null, true);
262 window
.localStorage
.setItem('ProxmoxUser', JSON
.stringify(data
));
267 if (Proxmox
.LoggedOut
) {
270 let storedAuth
= Proxmox
.Utils
.getStoredAuth();
271 let cookie
= Ext
.util
.Cookies
.get(Proxmox
.Setup
.auth_cookie_name
);
272 if ((Proxmox
.UserName
!== '' && cookie
&& !cookie
.startsWith("PVE:tfa!")) || storedAuth
.token
) {
273 return cookie
|| storedAuth
.token
;
279 authClear: function() {
280 if (Proxmox
.LoggedOut
) {
283 Ext
.util
.Cookies
.clear(Proxmox
.Setup
.auth_cookie_name
);
284 window
.localStorage
.removeItem("ProxmoxUser");
287 // comp.setLoading() is buggy in ExtJS 4.0.7, so we
288 // use el.mask() instead
289 setErrorMask: function(comp
, msg
) {
296 } else if (msg
=== true) {
297 el
.mask(gettext("Loading..."));
303 getResponseErrorMessage
: (err
) => {
304 if (!err
.statusText
) {
305 return gettext('Connection error');
307 let msg
= [`${err.statusText} (${err.status})`];
308 if (err
.response
&& err
.response
.responseText
) {
309 let txt
= err
.response
.responseText
;
311 let res
= JSON
.parse(txt
);
312 if (res
.errors
&& typeof res
.errors
=== 'object') {
313 for (let [key
, value
] of Object
.entries(res
.errors
)) {
314 msg
.push(Ext
.String
.htmlEncode(`${key}: ${value}`));
318 // fallback to string
319 msg
.push(Ext
.String
.htmlEncode(txt
));
322 return msg
.join('<br>');
325 monStoreErrors: function(component
, store
, clearMaskBeforeLoad
) {
326 if (clearMaskBeforeLoad
) {
327 component
.mon(store
, 'beforeload', function(s
, operation
, eOpts
) {
328 Proxmox
.Utils
.setErrorMask(component
, false);
331 component
.mon(store
, 'beforeload', function(s
, operation
, eOpts
) {
332 if (!component
.loadCount
) {
333 component
.loadCount
= 0; // make sure it is nucomponent.ic
334 Proxmox
.Utils
.setErrorMask(component
, true);
339 // only works with 'proxmox' proxy
340 component
.mon(store
.proxy
, 'afterload', function(proxy
, request
, success
) {
341 component
.loadCount
++;
344 Proxmox
.Utils
.setErrorMask(component
, false);
348 let error
= request
._operation
.getError();
349 let msg
= Proxmox
.Utils
.getResponseErrorMessage(error
);
350 Proxmox
.Utils
.setErrorMask(component
, msg
);
354 extractRequestError: function(result
, verbose
) {
355 let msg
= gettext('Successful');
357 if (!result
.success
) {
358 msg
= gettext("Unknown error");
359 if (result
.message
) {
360 msg
= result
.message
;
362 msg
+= ' (' + result
.status
+ ')';
365 if (verbose
&& Ext
.isObject(result
.errors
)) {
367 Ext
.Object
.each(result
.errors
, function(prop
, desc
) {
368 msg
+= "<br><b>" + Ext
.htmlEncode(prop
) + "</b>: " +
369 Ext
.htmlEncode(desc
);
378 API2Request: function(reqOpts
) {
379 let newopts
= Ext
.apply({
380 waitMsg
: gettext('Please wait...'),
383 if (!newopts
.url
.match(/^\/api2/)) {
384 newopts
.url
= '/api2/extjs' + newopts
.url
;
386 delete newopts
.callback
;
388 let createWrapper = function(successFn
, callbackFn
, failureFn
) {
390 success: function(response
, options
) {
391 if (options
.waitMsgTarget
) {
392 if (Proxmox
.Utils
.toolkit
=== 'touch') {
393 options
.waitMsgTarget
.setMasked(false);
395 options
.waitMsgTarget
.setLoading(false);
398 let result
= Ext
.decode(response
.responseText
);
399 response
.result
= result
;
400 if (!result
.success
) {
401 response
.htmlStatus
= Proxmox
.Utils
.extractRequestError(result
, true);
402 Ext
.callback(callbackFn
, options
.scope
, [options
, false, response
]);
403 Ext
.callback(failureFn
, options
.scope
, [response
, options
]);
406 Ext
.callback(callbackFn
, options
.scope
, [options
, true, response
]);
407 Ext
.callback(successFn
, options
.scope
, [response
, options
]);
409 failure: function(response
, options
) {
410 if (options
.waitMsgTarget
) {
411 if (Proxmox
.Utils
.toolkit
=== 'touch') {
412 options
.waitMsgTarget
.setMasked(false);
414 options
.waitMsgTarget
.setLoading(false);
417 response
.result
= {};
419 response
.result
= Ext
.decode(response
.responseText
);
423 let msg
= gettext('Connection error') + ' - server offline?';
424 if (response
.aborted
) {
425 msg
= gettext('Connection error') + ' - aborted.';
426 } else if (response
.timedout
) {
427 msg
= gettext('Connection error') + ' - Timeout.';
428 } else if (response
.status
&& response
.statusText
) {
429 msg
= gettext('Connection error') + ' ' + response
.status
+ ': ' + response
.statusText
;
431 response
.htmlStatus
= msg
;
432 Ext
.callback(callbackFn
, options
.scope
, [options
, false, response
]);
433 Ext
.callback(failureFn
, options
.scope
, [response
, options
]);
438 createWrapper(reqOpts
.success
, reqOpts
.callback
, reqOpts
.failure
);
440 let target
= newopts
.waitMsgTarget
;
442 if (Proxmox
.Utils
.toolkit
=== 'touch') {
443 target
.setMasked({ xtype
: 'loadmask', message
: newopts
.waitMsg
});
445 // Note: ExtJS bug - this does not work when component is not rendered
446 target
.setLoading(newopts
.waitMsg
);
449 Ext
.Ajax
.request(newopts
);
452 checked_command: function(orig_cmd
) {
453 Proxmox
.Utils
.API2Request(
455 url
: '/nodes/localhost/subscription',
457 failure: function(response
, opts
) {
458 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
460 success: function(response
, opts
) {
461 let res
= response
.result
;
462 if (res
=== null || res
=== undefined || !res
|| res
463 .data
.status
!== 'Active') {
465 title
: gettext('No valid subscription'),
466 icon
: Ext
.Msg
.WARNING
,
467 message
: Proxmox
.Utils
.getNoSubKeyHtml(res
.data
.url
),
469 callback: function(btn
) {
484 assemble_field_data: function(values
, data
) {
485 if (!Ext
.isObject(data
)) {
488 Ext
.Object
.each(data
, function(name
, val
) {
489 if (Object
.prototype.hasOwnProperty
.call(values
, name
)) {
490 let bucket
= values
[name
];
491 if (!Ext
.isArray(bucket
)) {
492 bucket
= values
[name
] = [bucket
];
494 if (Ext
.isArray(val
)) {
495 values
[name
] = bucket
.concat(val
);
505 updateColumnWidth: function(container
) {
506 let mode
= Ext
.state
.Manager
.get('summarycolumns') || 'auto';
508 if (mode
!== 'auto') {
509 factor
= parseInt(mode
, 10);
510 if (Number
.isNaN(factor
)) {
514 factor
= container
.getSize().width
< 1600 ? 1 : 2;
517 if (container
.oldFactor
=== factor
) {
521 let items
= container
.query('>'); // direct childs
522 factor
= Math
.min(factor
, items
.length
);
523 container
.oldFactor
= factor
;
525 items
.forEach((item
) => {
526 item
.columnWidth
= 1 / factor
;
529 // we have to update the layout twice, since the first layout change
530 // can trigger the scrollbar which reduces the amount of space left
531 container
.updateLayout();
532 container
.updateLayout();
535 dialog_title: function(subject
, create
, isAdd
) {
538 return gettext('Add') + ': ' + subject
;
540 return gettext('Create') + ': ' + subject
;
543 return gettext('Edit') + ': ' + subject
;
547 network_iface_types
: {
548 eth
: gettext("Network Device"),
549 bridge
: 'Linux Bridge',
552 OVSBridge
: 'OVS Bridge',
555 OVSIntPort
: 'OVS IntPort',
558 render_network_iface_type: function(value
) {
559 return Proxmox
.Utils
.network_iface_types
[value
] ||
560 Proxmox
.Utils
.unknownText
;
564 acmenewcert
: ['SRV', gettext('Order Certificate')],
565 acmeregister
: ['ACME Account', gettext('Register')],
566 acmedeactivate
: ['ACME Account', gettext('Deactivate')],
567 acmeupdate
: ['ACME Account', gettext('Update')],
568 acmerefresh
: ['ACME Account', gettext('Refresh')],
569 acmerenew
: ['SRV', gettext('Renew Certificate')],
570 acmerevoke
: ['SRV', gettext('Revoke Certificate')],
571 'auth-realm-sync': [gettext('Realm'), gettext('Sync')],
572 'auth-realm-sync-test': [gettext('Realm'), gettext('Sync Preview')],
573 'move_volume': ['CT', gettext('Move Volume')],
574 clustercreate
: ['', gettext('Create Cluster')],
575 clusterjoin
: ['', gettext('Join Cluster')],
576 diskinit
: ['Disk', gettext('Initialize Disk with GPT')],
577 vncproxy
: ['VM/CT', gettext('Console')],
578 spiceproxy
: ['VM/CT', gettext('Console') + ' (Spice)'],
579 vncshell
: ['', gettext('Shell')],
580 spiceshell
: ['', gettext('Shell') + ' (Spice)'],
581 qmsnapshot
: ['VM', gettext('Snapshot')],
582 qmrollback
: ['VM', gettext('Rollback')],
583 qmdelsnapshot
: ['VM', gettext('Delete Snapshot')],
584 qmcreate
: ['VM', gettext('Create')],
585 qmrestore
: ['VM', gettext('Restore')],
586 qmdestroy
: ['VM', gettext('Destroy')],
587 qmigrate
: ['VM', gettext('Migrate')],
588 qmclone
: ['VM', gettext('Clone')],
589 qmmove
: ['VM', gettext('Move disk')],
590 qmtemplate
: ['VM', gettext('Convert to template')],
591 qmstart
: ['VM', gettext('Start')],
592 qmstop
: ['VM', gettext('Stop')],
593 qmreset
: ['VM', gettext('Reset')],
594 qmshutdown
: ['VM', gettext('Shutdown')],
595 qmreboot
: ['VM', gettext('Reboot')],
596 qmsuspend
: ['VM', gettext('Hibernate')],
597 qmpause
: ['VM', gettext('Pause')],
598 qmresume
: ['VM', gettext('Resume')],
599 qmconfig
: ['VM', gettext('Configure')],
600 vzsnapshot
: ['CT', gettext('Snapshot')],
601 vzrollback
: ['CT', gettext('Rollback')],
602 vzdelsnapshot
: ['CT', gettext('Delete Snapshot')],
603 vzcreate
: ['CT', gettext('Create')],
604 vzrestore
: ['CT', gettext('Restore')],
605 vzdestroy
: ['CT', gettext('Destroy')],
606 vzmigrate
: ['CT', gettext('Migrate')],
607 vzclone
: ['CT', gettext('Clone')],
608 vztemplate
: ['CT', gettext('Convert to template')],
609 vzstart
: ['CT', gettext('Start')],
610 vzstop
: ['CT', gettext('Stop')],
611 vzmount
: ['CT', gettext('Mount')],
612 vzumount
: ['CT', gettext('Unmount')],
613 vzshutdown
: ['CT', gettext('Shutdown')],
614 vzreboot
: ['CT', gettext('Reboot')],
615 vzsuspend
: ['CT', gettext('Suspend')],
616 vzresume
: ['CT', gettext('Resume')],
617 push_file
: ['CT', gettext('Push file')],
618 pull_file
: ['CT', gettext('Pull file')],
619 hamigrate
: ['HA', gettext('Migrate')],
620 hastart
: ['HA', gettext('Start')],
621 hastop
: ['HA', gettext('Stop')],
622 hashutdown
: ['HA', gettext('Shutdown')],
623 srvstart
: ['SRV', gettext('Start')],
624 srvstop
: ['SRV', gettext('Stop')],
625 srvrestart
: ['SRV', gettext('Restart')],
626 srvreload
: ['SRV', gettext('Reload')],
627 cephcreatemgr
: ['Ceph Manager', gettext('Create')],
628 cephdestroymgr
: ['Ceph Manager', gettext('Destroy')],
629 cephcreatemon
: ['Ceph Monitor', gettext('Create')],
630 cephdestroymon
: ['Ceph Monitor', gettext('Destroy')],
631 cephcreateosd
: ['Ceph OSD', gettext('Create')],
632 cephdestroyosd
: ['Ceph OSD', gettext('Destroy')],
633 cephcreatepool
: ['Ceph Pool', gettext('Create')],
634 cephdestroypool
: ['Ceph Pool', gettext('Destroy')],
635 cephfscreate
: ['CephFS', gettext('Create')],
636 cephcreatemds
: ['Ceph Metadata Server', gettext('Create')],
637 cephdestroymds
: ['Ceph Metadata Server', gettext('Destroy')],
638 imgcopy
: ['', gettext('Copy data')],
639 imgdel
: ['', gettext('Erase data')],
640 unknownimgdel
: ['', gettext('Destroy image from unknown guest')],
641 download
: ['', gettext('Download')],
642 vzdump
: (type
, id
) => id
? `VM/CT ${id} - ${gettext('Backup')}` : gettext('Backup Job'),
643 aptupdate
: ['', gettext('Update package database')],
644 startall
: ['', gettext('Start all VMs and Containers')],
645 stopall
: ['', gettext('Stop all VMs and Containers')],
646 migrateall
: ['', gettext('Migrate all VMs and Containers')],
647 dircreate
: [gettext('Directory Storage'), gettext('Create')],
648 lvmcreate
: [gettext('LVM Storage'), gettext('Create')],
649 lvmthincreate
: [gettext('LVM-Thin Storage'), gettext('Create')],
650 zfscreate
: [gettext('ZFS Storage'), gettext('Create')],
653 // to add or change existing for product specific ones
654 override_task_descriptions: function(extra
) {
655 for (const [key
, value
] of Object
.entries(extra
)) {
656 Proxmox
.Utils
.task_desc_table
[key
] = value
;
660 format_task_description: function(type
, id
) {
661 let farray
= Proxmox
.Utils
.task_desc_table
[type
];
669 } else if (Ext
.isFunction(farray
)) {
670 return farray(type
, id
);
672 let prefix
= farray
[0];
674 if (prefix
&& id
!== undefined) {
675 return prefix
+ ' ' + id
+ ' - ' + text
;
680 format_size: function(size
) {
681 let units
= ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'];
683 while (size
>= 1024 && num
++ <= units
.length
) {
687 return size
.toFixed(num
> 0?2:0) + " " + units
[num
] + "B";
690 render_upid: function(value
, metaData
, record
) {
691 let task
= record
.data
;
692 let type
= task
.type
|| task
.worker_type
;
693 let id
= task
.id
|| task
.worker_id
;
695 return Proxmox
.Utils
.format_task_description(type
, id
);
698 render_uptime: function(value
) {
701 if (uptime
=== undefined) {
709 return Proxmox
.Utils
.format_duration_long(uptime
);
712 parse_task_upid: function(upid
) {
715 let res
= upid
.match(/^UPID:([^\s:]+):([0-9A-Fa-f]{8}):([0-9A-Fa-f]{8,9}):(([0-9A-Fa-f]{8,16}):)?([0-9A-Fa-f]{8}):([^:\s]+):([^:\s]*):([^:\s]+):$/);
717 throw "unable to parse upid '" + upid
+ "'";
720 task
.pid
= parseInt(res
[2], 16);
721 task
.pstart
= parseInt(res
[3], 16);
722 if (res
[5] !== undefined) {
723 task
.task_id
= parseInt(res
[5], 16);
725 task
.starttime
= parseInt(res
[6], 16);
730 task
.desc
= Proxmox
.Utils
.format_task_description(task
.type
, task
.id
);
735 parse_task_status: function(status
) {
736 if (status
=== 'OK') {
740 if (status
=== 'unknown') {
744 let match
= status
.match(/^WARNINGS: (.*)$/);
752 render_duration: function(value
) {
753 if (value
=== undefined) {
756 return Proxmox
.Utils
.format_duration_human(value
);
759 render_timestamp: function(value
, metaData
, record
, rowIndex
, colIndex
, store
) {
760 let servertime
= new Date(value
* 1000);
761 return Ext
.Date
.format(servertime
, 'Y-m-d H:i:s');
764 render_zfs_health: function(value
) {
765 if (typeof value
=== 'undefined') {
768 var iconCls
= 'question-circle';
772 iconCls
= 'check-circle good';
776 iconCls
= 'exclamation-circle warning';
781 iconCls
= 'times-circle critical';
786 return '<i class="fa fa-' + iconCls
+ '"></i> ' + value
;
789 get_help_info: function(section
) {
791 if (typeof proxmoxOnlineHelpInfo
!== 'undefined') {
792 helpMap
= proxmoxOnlineHelpInfo
; // eslint-disable-line no-undef
793 } else if (typeof pveOnlineHelpInfo
!== 'undefined') {
794 // be backward compatible with older pve-doc-generators
795 helpMap
= pveOnlineHelpInfo
; // eslint-disable-line no-undef
797 throw "no global OnlineHelpInfo map declared";
800 if (helpMap
[section
]) {
801 return helpMap
[section
];
803 // try to normalize - and _ separators, to support asciidoc and sphinx
804 // references at the same time.
805 let section_minus_normalized
= section
.replace(/_
/, '-');
806 if (helpMap
[section_minus_normalized
]) {
807 return helpMap
[section_minus_normalized
];
809 let section_underscore_normalized
= section
.replace(/-/, '_');
810 return helpMap
[section_underscore_normalized
];
813 get_help_link: function(section
) {
814 let info
= Proxmox
.Utils
.get_help_info(section
);
818 return window
.location
.origin
+ info
.link
;
821 openXtermJsViewer: function(vmtype
, vmid
, nodename
, vmname
, cmd
) {
822 let url
= Ext
.Object
.toQueryString({
823 console
: vmtype
, // kvm, lxc, upgrade or shell
831 let nw
= window
.open("?" + url
, '_blank', 'toolbar=no,location=no,status=no,menubar=no,resizable=yes,width=800,height=420');
840 constructor: function() {
842 Ext
.apply(me
, me
.utilities
);
844 let IPV4_OCTET
= "(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])";
845 let IPV4_REGEXP
= "(?:(?:" + IPV4_OCTET
+ "\\.){3}" + IPV4_OCTET
+ ")";
846 let IPV6_H16
= "(?:[0-9a-fA-F]{1,4})";
847 let IPV6_LS32
= "(?:(?:" + IPV6_H16
+ ":" + IPV6_H16
+ ")|" + IPV4_REGEXP
+ ")";
848 let IPV4_CIDR_MASK
= "([0-9]{1,2})";
849 let IPV6_CIDR_MASK
= "([0-9]{1,3})";
852 me
.IP4_match
= new RegExp("^(?:" + IPV4_REGEXP
+ ")$");
853 me
.IP4_cidr_match
= new RegExp("^(?:" + IPV4_REGEXP
+ ")/" + IPV4_CIDR_MASK
+ "$");
855 /* eslint-disable no-useless-concat,no-multi-spaces */
856 let IPV6_REGEXP
= "(?:" +
857 "(?:(?:" + "(?:" + IPV6_H16
+ ":){6})" + IPV6_LS32
+ ")|" +
858 "(?:(?:" + "::" + "(?:" + IPV6_H16
+ ":){5})" + IPV6_LS32
+ ")|" +
859 "(?:(?:(?:" + IPV6_H16
+ ")?::" + "(?:" + IPV6_H16
+ ":){4})" + IPV6_LS32
+ ")|" +
860 "(?:(?:(?:(?:" + IPV6_H16
+ ":){0,1}" + IPV6_H16
+ ")?::" + "(?:" + IPV6_H16
+ ":){3})" + IPV6_LS32
+ ")|" +
861 "(?:(?:(?:(?:" + IPV6_H16
+ ":){0,2}" + IPV6_H16
+ ")?::" + "(?:" + IPV6_H16
+ ":){2})" + IPV6_LS32
+ ")|" +
862 "(?:(?:(?:(?:" + IPV6_H16
+ ":){0,3}" + IPV6_H16
+ ")?::" + "(?:" + IPV6_H16
+ ":){1})" + IPV6_LS32
+ ")|" +
863 "(?:(?:(?:(?:" + IPV6_H16
+ ":){0,4}" + IPV6_H16
+ ")?::" + ")" + IPV6_LS32
+ ")|" +
864 "(?:(?:(?:(?:" + IPV6_H16
+ ":){0,5}" + IPV6_H16
+ ")?::" + ")" + IPV6_H16
+ ")|" +
865 "(?:(?:(?:(?:" + IPV6_H16
+ ":){0,7}" + IPV6_H16
+ ")?::" + ")" + ")" +
867 /* eslint-enable no-useless-concat,no-multi-spaces */
869 me
.IP6_match
= new RegExp("^(?:" + IPV6_REGEXP
+ ")$");
870 me
.IP6_cidr_match
= new RegExp("^(?:" + IPV6_REGEXP
+ ")/" + IPV6_CIDR_MASK
+ "$");
871 me
.IP6_bracket_match
= new RegExp("^\\[(" + IPV6_REGEXP
+ ")\\]");
873 me
.IP64_match
= new RegExp("^(?:" + IPV6_REGEXP
+ "|" + IPV4_REGEXP
+ ")$");
874 me
.IP64_cidr_match
= new RegExp("^(?:" + IPV6_REGEXP
+ "/" + IPV6_CIDR_MASK
+ ")|(?:" + IPV4_REGEXP
+ "/" + IPV4_CIDR_MASK
+ ")$");
876 let DnsName_REGEXP
= "(?:(([a-zA-Z0-9]([a-zA-Z0-9\\-]*[a-zA-Z0-9])?)\\.)*([A-Za-z0-9]([A-Za-z0-9\\-]*[A-Za-z0-9])?))";
877 me
.DnsName_match
= new RegExp("^" + DnsName_REGEXP
+ "$");
879 me
.HostPort_match
= new RegExp("^(" + IPV4_REGEXP
+ "|" + DnsName_REGEXP
+ ")(:\\d+)?$");
880 me
.HostPortBrackets_match
= new RegExp("^\\[(?:" + IPV6_REGEXP
+ "|" + IPV4_REGEXP
+ "|" + DnsName_REGEXP
+ ")\\](:\\d+)?$");
881 me
.IP6_dotnotation_match
= new RegExp("^" + IPV6_REGEXP
+ "(\\.\\d+)?$");
882 me
.Vlan_match
= /^vlan(\\d+)/;
883 me
.VlanInterface_match
= /(\\w+)\\.(\\d+)/;