From ecabd4379cdc61d0b8d3513fc53bce082d623827 Mon Sep 17 00:00:00 2001 From: Thomas Lamprecht Date: Sat, 6 Jun 2020 17:35:28 +0200 Subject: [PATCH] cleanly separate sources from package build, move to own folder compared result with `diffoscope`, saw now difference Signed-off-by: Thomas Lamprecht --- Makefile | 85 +- debian/control | 3 +- defines.mk | 15 - Logo.js => src/Logo.js | 0 src/Makefile | 73 ++ Toolkit.js => src/Toolkit.js | 0 src/Utils.js | 812 +++++++++++++++++++ {button => src/button}/Button.js | 0 {button => src/button}/HelpButton.js | 0 {css => src/css}/Makefile | 0 {css => src/css}/ext6-pmx.css | 0 {data => src/data}/DiffStore.js | 0 {data => src/data}/ObjectStore.js | 0 {data => src/data}/ProxmoxProxy.js | 0 {data => src/data}/RRDStore.js | 0 {data => src/data}/TimezoneStore.js | 0 {data => src/data}/UpdateStore.js | 0 {data => src/data}/model/Realm.js | 0 {data => src/data}/reader/JsonObject.js | 0 src/defines.mk | 7 + {form => src/form}/BondModeSelector.js | 0 {form => src/form}/Checkbox.js | 0 {form => src/form}/ComboGrid.js | 0 {form => src/form}/DateTimeField.js | 0 {form => src/form}/DisplayEdit.js | 0 {form => src/form}/ExpireDate.js | 0 {form => src/form}/IntegerField.js | 0 {form => src/form}/KVComboBox.js | 0 {form => src/form}/LanguageSelector.js | 0 {form => src/form}/NetworkSelector.js | 0 {form => src/form}/RRDTypeSelector.js | 0 {form => src/form}/RealmComboBox.js | 0 {form => src/form}/RoleSelector.js | 0 {form => src/form}/TextField.js | 0 {grid => src/grid}/ObjectGrid.js | 0 {grid => src/grid}/PendingObjectGrid.js | 0 {images => src/images}/Makefile | 0 {images => src/images}/pmx-clear-trigger.png | Bin {mixin => src/mixin}/CBind.js | 0 {node => src/node}/APT.js | 0 {node => src/node}/DNSEdit.js | 0 {node => src/node}/DNSView.js | 0 {node => src/node}/HostsView.js | 0 {node => src/node}/NetworkEdit.js | 0 {node => src/node}/NetworkView.js | 0 {node => src/node}/ServiceView.js | 0 {node => src/node}/Tasks.js | 0 {node => src/node}/TimeEdit.js | 0 {node => src/node}/TimeView.js | 0 {panel => src/panel}/GaugeWidget.js | 0 {panel => src/panel}/InputPanel.js | 0 {panel => src/panel}/JournalView.js | 0 {panel => src/panel}/LogView.js | 0 {panel => src/panel}/RRDChart.js | 0 {window => src/window}/Edit.js | 0 {window => src/window}/LanguageEdit.js | 0 {window => src/window}/PasswordEdit.js | 0 {window => src/window}/TaskViewer.js | 0 58 files changed, 906 insertions(+), 89 deletions(-) delete mode 100644 defines.mk rename Logo.js => src/Logo.js (100%) create mode 100644 src/Makefile rename Toolkit.js => src/Toolkit.js (100%) create mode 100644 src/Utils.js rename {button => src/button}/Button.js (100%) rename {button => src/button}/HelpButton.js (100%) rename {css => src/css}/Makefile (100%) rename {css => src/css}/ext6-pmx.css (100%) rename {data => src/data}/DiffStore.js (100%) rename {data => src/data}/ObjectStore.js (100%) rename {data => src/data}/ProxmoxProxy.js (100%) rename {data => src/data}/RRDStore.js (100%) rename {data => src/data}/TimezoneStore.js (100%) rename {data => src/data}/UpdateStore.js (100%) rename {data => src/data}/model/Realm.js (100%) rename {data => src/data}/reader/JsonObject.js (100%) create mode 100644 src/defines.mk rename {form => src/form}/BondModeSelector.js (100%) rename {form => src/form}/Checkbox.js (100%) rename {form => src/form}/ComboGrid.js (100%) rename {form => src/form}/DateTimeField.js (100%) rename {form => src/form}/DisplayEdit.js (100%) rename {form => src/form}/ExpireDate.js (100%) rename {form => src/form}/IntegerField.js (100%) rename {form => src/form}/KVComboBox.js (100%) rename {form => src/form}/LanguageSelector.js (100%) rename {form => src/form}/NetworkSelector.js (100%) rename {form => src/form}/RRDTypeSelector.js (100%) rename {form => src/form}/RealmComboBox.js (100%) rename {form => src/form}/RoleSelector.js (100%) rename {form => src/form}/TextField.js (100%) rename {grid => src/grid}/ObjectGrid.js (100%) rename {grid => src/grid}/PendingObjectGrid.js (100%) rename {images => src/images}/Makefile (100%) rename {images => src/images}/pmx-clear-trigger.png (100%) rename {mixin => src/mixin}/CBind.js (100%) rename {node => src/node}/APT.js (100%) rename {node => src/node}/DNSEdit.js (100%) rename {node => src/node}/DNSView.js (100%) rename {node => src/node}/HostsView.js (100%) rename {node => src/node}/NetworkEdit.js (100%) rename {node => src/node}/NetworkView.js (100%) rename {node => src/node}/ServiceView.js (100%) rename {node => src/node}/Tasks.js (100%) rename {node => src/node}/TimeEdit.js (100%) rename {node => src/node}/TimeView.js (100%) rename {panel => src/panel}/GaugeWidget.js (100%) rename {panel => src/panel}/InputPanel.js (100%) rename {panel => src/panel}/JournalView.js (100%) rename {panel => src/panel}/LogView.js (100%) rename {panel => src/panel}/RRDChart.js (100%) rename {window => src/window}/Edit.js (100%) rename {window => src/window}/LanguageEdit.js (100%) rename {window => src/window}/PasswordEdit.js (100%) rename {window => src/window}/TaskViewer.js (100%) diff --git a/Makefile b/Makefile index 1d1dafb..f3bd2cf 100644 --- a/Makefile +++ b/Makefile @@ -1,67 +1,18 @@ include /usr/share/dpkg/pkg-info.mk -include defines.mk +export PACKAGE=proxmox-widget-toolkit +BUILDDIR ?= ${PACKAGE}-${DEB_VERSION_UPSTREAM} +DEB=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}_all.deb +DSC=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}.dsc -SUBDIRS= css images - -JSSRC= \ - Utils.js \ - Toolkit.js \ - Logo.js \ - mixin/CBind.js \ - data/reader/JsonObject.js \ - data/ProxmoxProxy.js \ - data/UpdateStore.js \ - data/DiffStore.js \ - data/ObjectStore.js \ - data/RRDStore.js \ - data/TimezoneStore.js \ - data/model/Realm.js \ - form/DisplayEdit.js \ - form/ExpireDate.js \ - form/IntegerField.js \ - form/TextField.js \ - form/DateTimeField.js \ - form/Checkbox.js \ - form/KVComboBox.js \ - form/LanguageSelector.js \ - form/ComboGrid.js \ - form/RRDTypeSelector.js \ - form/BondModeSelector.js \ - form/NetworkSelector.js \ - form/RealmComboBox.js \ - form/RoleSelector.js \ - button/Button.js \ - button/HelpButton.js \ - grid/ObjectGrid.js \ - grid/PendingObjectGrid.js \ - panel/InputPanel.js \ - panel/LogView.js \ - panel/JournalView.js \ - panel/RRDChart.js \ - panel/GaugeWidget.js \ - window/Edit.js \ - window/PasswordEdit.js \ - window/TaskViewer.js \ - window/LanguageEdit.js \ - node/APT.js \ - node/NetworkEdit.js \ - node/NetworkView.js \ - node/DNSEdit.js \ - node/HostsView.js \ - node/DNSView.js \ - node/Tasks.js \ - node/ServiceView.js \ - node/TimeEdit.js \ - node/TimeView.js - -all: ${SUBDIRS} - set -e && for i in ${SUBDIRS}; do ${MAKE} -C $$i; done +GITVERSION:=$(shell git rev-parse HEAD) ${BUILDDIR}: - rm -rf ${BUILDDIR} - rsync -a * ${BUILDDIR} - echo "git clone git://git.proxmox.com/git/proxmox-widget-toolkit.git\\ngit checkout ${GITVERSION}" > ${BUILDDIR}/debian/SOURCE + rm -rf ${BUILDDIR} ${BUILDDIR}.tmp + cp -a src/ ${BUILDDIR}.tmp + cp -a debian ${BUILDDIR}.tmp/ + echo "git clone git://git.proxmox.com/git/proxmox-widget-toolkit.git\\ngit checkout ${GITVERSION}" > ${BUILDDIR}.tmp/debian/SOURCE + mv ${BUILDDIR}.tmp/ ${BUILDDIR} .PHONY: deb deb: ${DEB} @@ -76,20 +27,8 @@ ${DSC}: ${BUILDDIR} lintian ${DSC} .PHONY: lint -check: lint lint: ${JSSRC} - eslint ${JSSRC} - -proxmoxlib.js: ${JSSRC} - # add the version as comment in the file - echo "// ${DEB_VERSION_UPSTREAM_REVISION}" > $@.tmp - cat ${JSSRC} >> $@.tmp - mv $@.tmp $@ - -install: proxmoxlib.js - install -d -m 755 ${WWWBASEDIR} - install -m 0644 proxmoxlib.js ${WWWBASEDIR} - set -e && for i in ${SUBDIRS}; do ${MAKE} -C $$i $@; done + ${MAKE} -C src lint .PHONY: upload upload: ${DEB} @@ -97,7 +36,7 @@ upload: ${DEB} distclean: clean clean: - rm -rf ${BUILDDIR} *.tar.gz *.dsc *.deb *.changes *.buildinfo proxmoxlib.js + rm -rf ${BUILDDIR} ${BUILDDIR}.tmp *.tar.gz *.dsc *.deb *.changes *.buildinfo find . -name '*~' -exec rm {} ';' .PHONY: dinstall diff --git a/debian/control b/debian/control index 800192d..1f9f206 100644 --- a/debian/control +++ b/debian/control @@ -3,7 +3,8 @@ Section: web Priority: optional Maintainer: Proxmox Support Team Build-Depends: debhelper (>= 10~), - rsync + pve-eslint, + rsync, Standards-Version: 3.9.8 Homepage: http://www.proxmox.com diff --git a/defines.mk b/defines.mk deleted file mode 100644 index 99461e1..0000000 --- a/defines.mk +++ /dev/null @@ -1,15 +0,0 @@ -PACKAGE=proxmox-widget-toolkit - -BUILDDIR ?= ${PACKAGE}-${DEB_VERSION_UPSTREAM} -GITVERSION:=$(shell git rev-parse HEAD) - -DEB=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}_all.deb -DSC=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}.dsc - -DESTDIR= - -DOCDIR=${DESTDIR}/usr/share/doc/${PACKAGE} - -WWWBASEDIR=${DESTDIR}/usr/share/javascript/${PACKAGE} -WWWCSSDIR=${WWWBASEDIR}/css -WWWIMAGESDIR=${WWWBASEDIR}/images diff --git a/Logo.js b/src/Logo.js similarity index 100% rename from Logo.js rename to src/Logo.js diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..659e876 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,73 @@ +include defines.mk + +SUBDIRS= css images + +JSSRC= \ + Utils.js \ + Toolkit.js \ + Logo.js \ + mixin/CBind.js \ + data/reader/JsonObject.js \ + data/ProxmoxProxy.js \ + data/UpdateStore.js \ + data/DiffStore.js \ + data/ObjectStore.js \ + data/RRDStore.js \ + data/TimezoneStore.js \ + data/model/Realm.js \ + form/DisplayEdit.js \ + form/ExpireDate.js \ + form/IntegerField.js \ + form/TextField.js \ + form/DateTimeField.js \ + form/Checkbox.js \ + form/KVComboBox.js \ + form/LanguageSelector.js \ + form/ComboGrid.js \ + form/RRDTypeSelector.js \ + form/BondModeSelector.js \ + form/NetworkSelector.js \ + form/RealmComboBox.js \ + form/RoleSelector.js \ + button/Button.js \ + button/HelpButton.js \ + grid/ObjectGrid.js \ + grid/PendingObjectGrid.js \ + panel/InputPanel.js \ + panel/LogView.js \ + panel/JournalView.js \ + panel/RRDChart.js \ + panel/GaugeWidget.js \ + window/Edit.js \ + window/PasswordEdit.js \ + window/TaskViewer.js \ + window/LanguageEdit.js \ + node/APT.js \ + node/NetworkEdit.js \ + node/NetworkView.js \ + node/DNSEdit.js \ + node/HostsView.js \ + node/DNSView.js \ + node/Tasks.js \ + node/ServiceView.js \ + node/TimeEdit.js \ + node/TimeView.js + +all: ${SUBDIRS} + set -e && for i in ${SUBDIRS}; do ${MAKE} -C $$i; done + +.PHONY: lint +check: lint +lint: ${JSSRC} + eslint ${JSSRC} + +proxmoxlib.js: ${JSSRC} + # add the version as comment in the file + echo "// ${DEB_VERSION_UPSTREAM_REVISION}" > $@.tmp + cat ${JSSRC} >> $@.tmp + mv $@.tmp $@ + +install: proxmoxlib.js + install -d -m 755 ${WWWBASEDIR} + install -m 0644 proxmoxlib.js ${WWWBASEDIR} + set -e && for i in ${SUBDIRS}; do ${MAKE} -C $$i $@; done diff --git a/Toolkit.js b/src/Toolkit.js similarity index 100% rename from Toolkit.js rename to src/Toolkit.js diff --git a/src/Utils.js b/src/Utils.js new file mode 100644 index 0000000..c3b13f4 --- /dev/null +++ b/src/Utils.js @@ -0,0 +1,812 @@ +Ext.ns('Proxmox'); +Ext.ns('Proxmox.Setup'); + +if (!Ext.isDefined(Proxmox.Setup.auth_cookie_name)) { + throw "Proxmox library not initialized"; +} + +// avoid errors related to Accessible Rich Internet Applications +// (access for people with disabilities) +// TODO reenable after all components are upgraded +Ext.enableAria = false; +Ext.enableAriaButtons = false; +Ext.enableAriaPanels = false; + +// avoid errors when running without development tools +if (!Ext.isDefined(Ext.global.console)) { + let console = { + dir: function() { + // do nothing + }, + log: function() { + // do nothing + }, + warn: function() { + // do nothing + }, + }; + Ext.global.console = console; +} + +Ext.Ajax.defaultHeaders = { + 'Accept': 'application/json', +}; + +Ext.Ajax.on('beforerequest', function(conn, options) { + if (Proxmox.CSRFPreventionToken) { + if (!options.headers) { + options.headers = {}; + } + options.headers.CSRFPreventionToken = Proxmox.CSRFPreventionToken; + } +}); + +Ext.define('Proxmox.Utils', { // a singleton +utilities: { + + yesText: gettext('Yes'), + noText: gettext('No'), + enabledText: gettext('Enabled'), + disabledText: gettext('Disabled'), + noneText: gettext('none'), + NoneText: gettext('None'), + errorText: gettext('Error'), + unknownText: gettext('Unknown'), + defaultText: gettext('Default'), + daysText: gettext('days'), + dayText: gettext('day'), + runningText: gettext('running'), + stoppedText: gettext('stopped'), + neverText: gettext('never'), + totalText: gettext('Total'), + usedText: gettext('Used'), + directoryText: gettext('Directory'), + stateText: gettext('State'), + groupText: gettext('Group'), + + language_map: { + ar: 'Arabic', + ca: 'Catalan', + da: 'Danish', + de: 'German', + en: 'English', + es: 'Spanish', + eu: 'Euskera (Basque)', + fa: 'Persian (Farsi)', + fr: 'French', + he: 'Hebrew', + it: 'Italian', + ja: 'Japanese', + nb: 'Norwegian (Bokmal)', + nn: 'Norwegian (Nynorsk)', + pl: 'Polish', + pt_BR: 'Portuguese (Brazil)', + ru: 'Russian', + sl: 'Slovenian', + sv: 'Swedish', + tr: 'Turkish', + zh_CN: 'Chinese (Simplified)', + zh_TW: 'Chinese (Traditional)', + }, + + render_language: function(value) { + if (!value) { + return Proxmox.Utils.defaultText + ' (English)'; + } + let text = Proxmox.Utils.language_map[value]; + if (text) { + return text + ' (' + value + ')'; + } + return value; + }, + + language_array: function() { + let data = [['__default__', Proxmox.Utils.render_language('')]]; + Ext.Object.each(Proxmox.Utils.language_map, function(key, value) { + data.push([key, Proxmox.Utils.render_language(value)]); + }); + + return data; + }, + + bond_mode_gettext_map: { + '802.3ad': 'LACP (802.3ad)', + 'lacp-balance-slb': 'LACP (balance-slb)', + 'lacp-balance-tcp': 'LACP (balance-tcp)', + }, + + render_bond_mode: value => Proxmox.Utils.bond_mode_gettext_map[value] || value || '', + + bond_mode_array: function(modes) { + return modes.map(mode => [mode, Proxmox.Utils.render_bond_mode(mode)]); + }, + + getNoSubKeyHtml: function(url) { + // url http://www.proxmox.com/products/proxmox-ve/subscription-service-plans + return Ext.String.format('You do not have a valid subscription for this server. Please visit www.proxmox.com to get a list of available options.', url || 'https://www.proxmox.com'); + }, + + format_boolean_with_default: function(value) { + if (Ext.isDefined(value) && value !== '__default__') { + return value ? Proxmox.Utils.yesText : Proxmox.Utils.noText; + } + return Proxmox.Utils.defaultText; + }, + + format_boolean: function(value) { + return value ? Proxmox.Utils.yesText : Proxmox.Utils.noText; + }, + + format_neg_boolean: function(value) { + return !value ? Proxmox.Utils.yesText : Proxmox.Utils.noText; + }, + + format_enabled_toggle: function(value) { + return value ? Proxmox.Utils.enabledText : Proxmox.Utils.disabledText; + }, + + format_expire: function(date) { + if (!date) { + return Proxmox.Utils.neverText; + } + return Ext.Date.format(date, "Y-m-d"); + }, + + // somewhat like a human would tell durations, omit zero values and do not + // give seconds precision if we talk days already + format_duration_human: function(ut) { + let seconds = 0, minutes = 0, hours = 0, days = 0; + + if (ut <= 0) { + return '0s'; + } + + let remaining = ut; + seconds = Number((remaining % 60).toFixed(1)); + remaining = Math.trunc(remaining / 60); + if (remaining > 0) { + minutes = remaining % 60; + remaining = Math.trunc(remaining / 60); + if (remaining > 0) { + hours = remaining % 24; + remaining = Math.trunc(remaining / 24); + if (remaining > 0) { + days = remaining; + } + } + } + + let res = []; + let add = (t, unit) => { + if (t > 0) res.push(t + unit); + return t > 0; + }; + + let addSeconds = !add(days, 'd'); + add(hours, 'h'); + add(minutes, 'm'); + if (addSeconds) { + add(seconds, 's'); + } + return res.join(' '); + }, + + format_duration_long: function(ut) { + let days = Math.floor(ut / 86400); + ut -= days*86400; + let hours = Math.floor(ut / 3600); + ut -= hours*3600; + let mins = Math.floor(ut / 60); + ut -= mins*60; + + let hours_str = '00' + hours.toString(); + hours_str = hours_str.substr(hours_str.length - 2); + let mins_str = "00" + mins.toString(); + mins_str = mins_str.substr(mins_str.length - 2); + let ut_str = "00" + ut.toString(); + ut_str = ut_str.substr(ut_str.length - 2); + + if (days) { + let ds = days > 1 ? Proxmox.Utils.daysText : Proxmox.Utils.dayText; + return days.toString() + ' ' + ds + ' ' + + hours_str + ':' + mins_str + ':' + ut_str; + } else { + return hours_str + ':' + mins_str + ':' + ut_str; + } + }, + + format_subscription_level: function(level) { + if (level === 'c') { + return 'Community'; + } else if (level === 'b') { + return 'Basic'; + } else if (level === 's') { + return 'Standard'; + } else if (level === 'p') { + return 'Premium'; + } else { + return Proxmox.Utils.noneText; + } + }, + + compute_min_label_width: function(text, width) { + if (width === undefined) { width = 100; } + + let tm = new Ext.util.TextMetrics(); + let min = tm.getWidth(text + ':'); + + return min < width ? width : min; + }, + + setAuthData: function(data) { + Proxmox.CSRFPreventionToken = data.CSRFPreventionToken; + Proxmox.UserName = data.username; + Proxmox.LoggedOut = data.LoggedOut; + // creates a session cookie (expire = null) + // that way the cookie gets deleted after the browser window is closed + Ext.util.Cookies.set(Proxmox.Setup.auth_cookie_name, data.ticket, null, '/', null, true); + }, + + authOK: function() { + if (Proxmox.LoggedOut) { + return undefined; + } + let cookie = Ext.util.Cookies.get(Proxmox.Setup.auth_cookie_name); + if (Proxmox.UserName !== '' && cookie && !cookie.startsWith("PVE:tfa!")) { + return cookie; + } else { + return false; + } + }, + + authClear: function() { + if (Proxmox.LoggedOut) { + return; + } + Ext.util.Cookies.clear(Proxmox.Setup.auth_cookie_name); + }, + + // comp.setLoading() is buggy in ExtJS 4.0.7, so we + // use el.mask() instead + setErrorMask: function(comp, msg) { + let el = comp.el; + if (!el) { + return; + } + if (!msg) { + el.unmask(); + } else if (msg === true) { + el.mask(gettext("Loading...")); + } else { + el.mask(msg); + } + }, + + getResponseErrorMessage: (err) => { + if (!err.statusText) { + return gettext('Connection error'); + } + let msg = [`${err.statusText} (${err.status})`]; + if (err.response && err.response.responseText) { + let txt = err.response.responseText; + try { + let res = JSON.parse(txt); + if (res.errors && typeof res.errors === 'object') { + for (let [key, value] of Object.entries(res.errors)) { + msg.push(Ext.String.htmlEncode(`${key}: ${value}`)); + } + } + } catch (e) { + // fallback to string + msg.push(Ext.String.htmlEncode(txt)); + } + } + return msg.join('
'); + }, + + monStoreErrors: function(component, store, clearMaskBeforeLoad) { + if (clearMaskBeforeLoad) { + component.mon(store, 'beforeload', function(s, operation, eOpts) { + Proxmox.Utils.setErrorMask(component, false); + }); + } else { + component.mon(store, 'beforeload', function(s, operation, eOpts) { + if (!component.loadCount) { + component.loadCount = 0; // make sure it is nucomponent.ic + Proxmox.Utils.setErrorMask(component, true); + } + }); + } + + // only works with 'proxmox' proxy + component.mon(store.proxy, 'afterload', function(proxy, request, success) { + component.loadCount++; + + if (success) { + Proxmox.Utils.setErrorMask(component, false); + return; + } + + let error = request._operation.getError(); + let msg = Proxmox.Utils.getResponseErrorMessage(error); + Proxmox.Utils.setErrorMask(component, msg); + }); + }, + + extractRequestError: function(result, verbose) { + let msg = gettext('Successful'); + + if (!result.success) { + msg = gettext("Unknown error"); + if (result.message) { + msg = result.message; + if (result.status) { + msg += ' (' + result.status + ')'; + } + } + if (verbose && Ext.isObject(result.errors)) { + msg += "
"; + Ext.Object.each(result.errors, function(prop, desc) { + msg += "
" + Ext.htmlEncode(prop) + ": " + + Ext.htmlEncode(desc); + }); + } + } + + return msg; + }, + + // Ext.Ajax.request + API2Request: function(reqOpts) { + let newopts = Ext.apply({ + waitMsg: gettext('Please wait...'), + }, reqOpts); + + if (!newopts.url.match(/^\/api2/)) { + newopts.url = '/api2/extjs' + newopts.url; + } + delete newopts.callback; + + let createWrapper = function(successFn, callbackFn, failureFn) { + Ext.apply(newopts, { + success: function(response, options) { + if (options.waitMsgTarget) { + if (Proxmox.Utils.toolkit === 'touch') { + options.waitMsgTarget.setMasked(false); + } else { + options.waitMsgTarget.setLoading(false); + } + } + let result = Ext.decode(response.responseText); + response.result = result; + if (!result.success) { + response.htmlStatus = Proxmox.Utils.extractRequestError(result, true); + Ext.callback(callbackFn, options.scope, [options, false, response]); + Ext.callback(failureFn, options.scope, [response, options]); + return; + } + Ext.callback(callbackFn, options.scope, [options, true, response]); + Ext.callback(successFn, options.scope, [response, options]); + }, + failure: function(response, options) { + if (options.waitMsgTarget) { + if (Proxmox.Utils.toolkit === 'touch') { + options.waitMsgTarget.setMasked(false); + } else { + options.waitMsgTarget.setLoading(false); + } + } + response.result = {}; + try { + response.result = Ext.decode(response.responseText); + } catch (e) { + // ignore + } + let msg = gettext('Connection error') + ' - server offline?'; + if (response.aborted) { + msg = gettext('Connection error') + ' - aborted.'; + } else if (response.timedout) { + msg = gettext('Connection error') + ' - Timeout.'; + } else if (response.status && response.statusText) { + msg = gettext('Connection error') + ' ' + response.status + ': ' + response.statusText; + } + response.htmlStatus = msg; + Ext.callback(callbackFn, options.scope, [options, false, response]); + Ext.callback(failureFn, options.scope, [response, options]); + }, + }); + }; + + createWrapper(reqOpts.success, reqOpts.callback, reqOpts.failure); + + let target = newopts.waitMsgTarget; + if (target) { + if (Proxmox.Utils.toolkit === 'touch') { + target.setMasked({ xtype: 'loadmask', message: newopts.waitMsg }); + } else { + // Note: ExtJS bug - this does not work when component is not rendered + target.setLoading(newopts.waitMsg); + } + } + Ext.Ajax.request(newopts); + }, + + checked_command: function(orig_cmd) { + Proxmox.Utils.API2Request({ + url: '/nodes/localhost/subscription', + method: 'GET', + failure: function(response, opts) { + Ext.Msg.alert(gettext('Error'), response.htmlStatus); + }, + success: function(response, opts) { + let data = response.result.data; + if (data.status !== 'Active') { + Ext.Msg.show({ + title: gettext('No valid subscription'), + icon: Ext.Msg.WARNING, + message: Proxmox.Utils.getNoSubKeyHtml(data.url), + buttons: Ext.Msg.OK, + callback: function(btn) { + if (btn !== 'ok') { + return; + } + orig_cmd(); + }, + }); + } else { + orig_cmd(); + } + }, + }); + }, + + assemble_field_data: function(values, data) { + if (!Ext.isObject(data)) { + return; + } + Ext.Object.each(data, function(name, val) { + if (Object.prototype.hasOwnProperty.call(values, name)) { + let bucket = values[name]; + if (!Ext.isArray(bucket)) { + bucket = values[name] = [bucket]; + } + if (Ext.isArray(val)) { + values[name] = bucket.concat(val); + } else { + bucket.push(val); + } + } else { + values[name] = val; + } + }); + }, + + updateColumnWidth: function(container) { + let mode = Ext.state.Manager.get('summarycolumns') || 'auto'; + let factor; + if (mode !== 'auto') { + factor = parseInt(mode, 10); + if (Number.isNaN(factor)) { + factor = 1; + } + } else { + factor = container.getSize().width < 1600 ? 1 : 2; + } + + if (container.oldFactor === factor) { + return; + } + + let items = container.query('>'); // direct childs + factor = Math.min(factor, items.length); + container.oldFactor = factor; + + items.forEach((item) => { + item.columnWidth = 1 / factor; + }); + + // we have to update the layout twice, since the first layout change + // can trigger the scrollbar which reduces the amount of space left + container.updateLayout(); + container.updateLayout(); + }, + + dialog_title: function(subject, create, isAdd) { + if (create) { + if (isAdd) { + return gettext('Add') + ': ' + subject; + } else { + return gettext('Create') + ': ' + subject; + } + } else { + return gettext('Edit') + ': ' + subject; + } + }, + + network_iface_types: { + eth: gettext("Network Device"), + bridge: 'Linux Bridge', + bond: 'Linux Bond', + vlan: 'Linux VLAN', + OVSBridge: 'OVS Bridge', + OVSBond: 'OVS Bond', + OVSPort: 'OVS Port', + OVSIntPort: 'OVS IntPort', + }, + + render_network_iface_type: function(value) { + return Proxmox.Utils.network_iface_types[value] || + Proxmox.Utils.unknownText; + }, + + task_desc_table: { + acmenewcert: ['SRV', gettext('Order Certificate')], + acmeregister: ['ACME Account', gettext('Register')], + acmedeactivate: ['ACME Account', gettext('Deactivate')], + acmeupdate: ['ACME Account', gettext('Update')], + acmerefresh: ['ACME Account', gettext('Refresh')], + acmerenew: ['SRV', gettext('Renew Certificate')], + acmerevoke: ['SRV', gettext('Revoke Certificate')], + 'auth-realm-sync': [gettext('Realm'), gettext('Sync')], + 'auth-realm-sync-test': [gettext('Realm'), gettext('Sync Preview')], + 'move_volume': ['CT', gettext('Move Volume')], + clustercreate: ['', gettext('Create Cluster')], + clusterjoin: ['', gettext('Join Cluster')], + diskinit: ['Disk', gettext('Initialize Disk with GPT')], + vncproxy: ['VM/CT', gettext('Console')], + spiceproxy: ['VM/CT', gettext('Console') + ' (Spice)'], + vncshell: ['', gettext('Shell')], + spiceshell: ['', gettext('Shell') + ' (Spice)'], + qmsnapshot: ['VM', gettext('Snapshot')], + qmrollback: ['VM', gettext('Rollback')], + qmdelsnapshot: ['VM', gettext('Delete Snapshot')], + qmcreate: ['VM', gettext('Create')], + qmrestore: ['VM', gettext('Restore')], + qmdestroy: ['VM', gettext('Destroy')], + qmigrate: ['VM', gettext('Migrate')], + qmclone: ['VM', gettext('Clone')], + qmmove: ['VM', gettext('Move disk')], + qmtemplate: ['VM', gettext('Convert to template')], + qmstart: ['VM', gettext('Start')], + qmstop: ['VM', gettext('Stop')], + qmreset: ['VM', gettext('Reset')], + qmshutdown: ['VM', gettext('Shutdown')], + qmreboot: ['VM', gettext('Reboot')], + qmsuspend: ['VM', gettext('Hibernate')], + qmpause: ['VM', gettext('Pause')], + qmresume: ['VM', gettext('Resume')], + qmconfig: ['VM', gettext('Configure')], + vzsnapshot: ['CT', gettext('Snapshot')], + vzrollback: ['CT', gettext('Rollback')], + vzdelsnapshot: ['CT', gettext('Delete Snapshot')], + vzcreate: ['CT', gettext('Create')], + vzrestore: ['CT', gettext('Restore')], + vzdestroy: ['CT', gettext('Destroy')], + vzmigrate: ['CT', gettext('Migrate')], + vzclone: ['CT', gettext('Clone')], + vztemplate: ['CT', gettext('Convert to template')], + vzstart: ['CT', gettext('Start')], + vzstop: ['CT', gettext('Stop')], + vzmount: ['CT', gettext('Mount')], + vzumount: ['CT', gettext('Unmount')], + vzshutdown: ['CT', gettext('Shutdown')], + vzreboot: ['CT', gettext('Reboot')], + vzsuspend: ['CT', gettext('Suspend')], + vzresume: ['CT', gettext('Resume')], + push_file: ['CT', gettext('Push file')], + pull_file: ['CT', gettext('Pull file')], + hamigrate: ['HA', gettext('Migrate')], + hastart: ['HA', gettext('Start')], + hastop: ['HA', gettext('Stop')], + hashutdown: ['HA', gettext('Shutdown')], + srvstart: ['SRV', gettext('Start')], + srvstop: ['SRV', gettext('Stop')], + srvrestart: ['SRV', gettext('Restart')], + srvreload: ['SRV', gettext('Reload')], + cephcreatemgr: ['Ceph Manager', gettext('Create')], + cephdestroymgr: ['Ceph Manager', gettext('Destroy')], + cephcreatemon: ['Ceph Monitor', gettext('Create')], + cephdestroymon: ['Ceph Monitor', gettext('Destroy')], + cephcreateosd: ['Ceph OSD', gettext('Create')], + cephdestroyosd: ['Ceph OSD', gettext('Destroy')], + cephcreatepool: ['Ceph Pool', gettext('Create')], + cephdestroypool: ['Ceph Pool', gettext('Destroy')], + cephfscreate: ['CephFS', gettext('Create')], + cephcreatemds: ['Ceph Metadata Server', gettext('Create')], + cephdestroymds: ['Ceph Metadata Server', gettext('Destroy')], + imgcopy: ['', gettext('Copy data')], + imgdel: ['', gettext('Erase data')], + unknownimgdel: ['', gettext('Destroy image from unknown guest')], + download: ['', gettext('Download')], + vzdump: (type, id) => id ? `VM/CT ${id} - ${gettext('Backup')}` : gettext('Backup Job'), + aptupdate: ['', gettext('Update package database')], + startall: ['', gettext('Start all VMs and Containers')], + stopall: ['', gettext('Stop all VMs and Containers')], + migrateall: ['', gettext('Migrate all VMs and Containers')], + dircreate: [gettext('Directory Storage'), gettext('Create')], + lvmcreate: [gettext('LVM Storage'), gettext('Create')], + lvmthincreate: [gettext('LVM-Thin Storage'), gettext('Create')], + zfscreate: [gettext('ZFS Storage'), gettext('Create')], + }, + + // to add or change existing for product specific ones + override_task_descriptions: function(extra) { + for (const [key, value] of Object.entries(extra)) { + Proxmox.Utils.task_desc_table[key] = value; + } + }, + + format_task_description: function(type, id) { + let farray = Proxmox.Utils.task_desc_table[type]; + let text; + if (!farray) { + text = type; + if (id) { + type += ' ' + id; + } + return text; + } else if (Ext.isFunction(farray)) { + return farray(type, id); + } + let prefix = farray[0]; + text = farray[1]; + if (prefix && id !== undefined) { + return prefix + ' ' + id + ' - ' + text; + } + return text; + }, + + format_size: function(size) { + let units = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']; + let num = 0; + while (size >= 1024 && num++ <= units.length) { + size = size / 1024; + } + + return size.toFixed(num > 0?2:0) + " " + units[num] + "B"; + }, + + render_upid: function(value, metaData, record) { + let task = record.data; + let type = task.type || task.worker_type; + let id = task.id || task.worker_id; + + return Proxmox.Utils.format_task_description(type, id); + }, + + render_uptime: function(value) { + let uptime = value; + + if (uptime === undefined) { + return ''; + } + + if (uptime <= 0) { + return '-'; + } + + return Proxmox.Utils.format_duration_long(uptime); + }, + + parse_task_upid: function(upid) { + let task = {}; + + 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]+):$/); + if (!res) { + throw "unable to parse upid '" + upid + "'"; + } + task.node = res[1]; + task.pid = parseInt(res[2], 16); + task.pstart = parseInt(res[3], 16); + if (res[5] !== undefined) { + task.task_id = parseInt(res[5], 16); + } + task.starttime = parseInt(res[6], 16); + task.type = res[7]; + task.id = res[8]; + task.user = res[9]; + + task.desc = Proxmox.Utils.format_task_description(task.type, task.id); + + return task; + }, + + render_duration: function(value) { + if (value === undefined) { + return '-'; + } + return Proxmox.Utils.format_duration_human(value); + }, + + render_timestamp: function(value, metaData, record, rowIndex, colIndex, store) { + let servertime = new Date(value * 1000); + return Ext.Date.format(servertime, 'Y-m-d H:i:s'); + }, + + get_help_info: function(section) { + let helpMap; + if (typeof proxmoxOnlineHelpInfo !== 'undefined') { + helpMap = proxmoxOnlineHelpInfo; // eslint-disable-line no-undef + } else if (typeof pveOnlineHelpInfo !== 'undefined') { + // be backward compatible with older pve-doc-generators + helpMap = pveOnlineHelpInfo; // eslint-disable-line no-undef + } else { + throw "no global OnlineHelpInfo map declared"; + } + + return helpMap[section]; + }, + + get_help_link: function(section) { + let info = Proxmox.Utils.get_help_info(section); + if (!info) { + return undefined; + } + return window.location.origin + info.link; + }, + + openXtermJsViewer: function(vmtype, vmid, nodename, vmname, cmd) { + let url = Ext.Object.toQueryString({ + console: vmtype, // kvm, lxc, upgrade or shell + xtermjs: 1, + vmid: vmid, + vmname: vmname, + node: nodename, + cmd: cmd, + + }); + let nw = window.open("?" + url, '_blank', 'toolbar=no,location=no,status=no,menubar=no,resizable=yes,width=800,height=420'); + if (nw) { + nw.focus(); + } + }, + +}, + + singleton: true, + constructor: function() { + let me = this; + Ext.apply(me, me.utilities); + + let IPV4_OCTET = "(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])"; + let IPV4_REGEXP = "(?:(?:" + IPV4_OCTET + "\\.){3}" + IPV4_OCTET + ")"; + let IPV6_H16 = "(?:[0-9a-fA-F]{1,4})"; + let IPV6_LS32 = "(?:(?:" + IPV6_H16 + ":" + IPV6_H16 + ")|" + IPV4_REGEXP + ")"; + let IPV4_CIDR_MASK = "([0-9]{1,2})"; + let IPV6_CIDR_MASK = "([0-9]{1,3})"; + + + me.IP4_match = new RegExp("^(?:" + IPV4_REGEXP + ")$"); + me.IP4_cidr_match = new RegExp("^(?:" + IPV4_REGEXP + ")/" + IPV4_CIDR_MASK + "$"); + + /* eslint-disable no-useless-concat,no-multi-spaces */ + let IPV6_REGEXP = "(?:" + + "(?:(?:" + "(?:" + IPV6_H16 + ":){6})" + IPV6_LS32 + ")|" + + "(?:(?:" + "::" + "(?:" + IPV6_H16 + ":){5})" + IPV6_LS32 + ")|" + + "(?:(?:(?:" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){4})" + IPV6_LS32 + ")|" + + "(?:(?:(?:(?:" + IPV6_H16 + ":){0,1}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){3})" + IPV6_LS32 + ")|" + + "(?:(?:(?:(?:" + IPV6_H16 + ":){0,2}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){2})" + IPV6_LS32 + ")|" + + "(?:(?:(?:(?:" + IPV6_H16 + ":){0,3}" + IPV6_H16 + ")?::" + "(?:" + IPV6_H16 + ":){1})" + IPV6_LS32 + ")|" + + "(?:(?:(?:(?:" + IPV6_H16 + ":){0,4}" + IPV6_H16 + ")?::" + ")" + IPV6_LS32 + ")|" + + "(?:(?:(?:(?:" + IPV6_H16 + ":){0,5}" + IPV6_H16 + ")?::" + ")" + IPV6_H16 + ")|" + + "(?:(?:(?:(?:" + IPV6_H16 + ":){0,7}" + IPV6_H16 + ")?::" + ")" + ")" + + ")"; + /* eslint-enable no-useless-concat,no-multi-spaces */ + + me.IP6_match = new RegExp("^(?:" + IPV6_REGEXP + ")$"); + me.IP6_cidr_match = new RegExp("^(?:" + IPV6_REGEXP + ")/" + IPV6_CIDR_MASK + "$"); + me.IP6_bracket_match = new RegExp("^\\[(" + IPV6_REGEXP + ")\\]"); + + me.IP64_match = new RegExp("^(?:" + IPV6_REGEXP + "|" + IPV4_REGEXP + ")$"); + me.IP64_cidr_match = new RegExp("^(?:" + IPV6_REGEXP + "/" + IPV6_CIDR_MASK + ")|(?:" + IPV4_REGEXP + "/" + IPV4_CIDR_MASK + ")$"); + + 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])?))"; + me.DnsName_match = new RegExp("^" + DnsName_REGEXP + "$"); + + me.HostPort_match = new RegExp("^(" + IPV4_REGEXP + "|" + DnsName_REGEXP + ")(:\\d+)?$"); + me.HostPortBrackets_match = new RegExp("^\\[(?:" + IPV6_REGEXP + "|" + IPV4_REGEXP + "|" + DnsName_REGEXP + ")\\](:\\d+)?$"); + me.IP6_dotnotation_match = new RegExp("^" + IPV6_REGEXP + "(\\.\\d+)?$"); + me.Vlan_match = /^vlan(\\d+)/; + me.VlanInterface_match = /(\\w+)\\.(\\d+)/; + }, +}); diff --git a/button/Button.js b/src/button/Button.js similarity index 100% rename from button/Button.js rename to src/button/Button.js diff --git a/button/HelpButton.js b/src/button/HelpButton.js similarity index 100% rename from button/HelpButton.js rename to src/button/HelpButton.js diff --git a/css/Makefile b/src/css/Makefile similarity index 100% rename from css/Makefile rename to src/css/Makefile diff --git a/css/ext6-pmx.css b/src/css/ext6-pmx.css similarity index 100% rename from css/ext6-pmx.css rename to src/css/ext6-pmx.css diff --git a/data/DiffStore.js b/src/data/DiffStore.js similarity index 100% rename from data/DiffStore.js rename to src/data/DiffStore.js diff --git a/data/ObjectStore.js b/src/data/ObjectStore.js similarity index 100% rename from data/ObjectStore.js rename to src/data/ObjectStore.js diff --git a/data/ProxmoxProxy.js b/src/data/ProxmoxProxy.js similarity index 100% rename from data/ProxmoxProxy.js rename to src/data/ProxmoxProxy.js diff --git a/data/RRDStore.js b/src/data/RRDStore.js similarity index 100% rename from data/RRDStore.js rename to src/data/RRDStore.js diff --git a/data/TimezoneStore.js b/src/data/TimezoneStore.js similarity index 100% rename from data/TimezoneStore.js rename to src/data/TimezoneStore.js diff --git a/data/UpdateStore.js b/src/data/UpdateStore.js similarity index 100% rename from data/UpdateStore.js rename to src/data/UpdateStore.js diff --git a/data/model/Realm.js b/src/data/model/Realm.js similarity index 100% rename from data/model/Realm.js rename to src/data/model/Realm.js diff --git a/data/reader/JsonObject.js b/src/data/reader/JsonObject.js similarity index 100% rename from data/reader/JsonObject.js rename to src/data/reader/JsonObject.js diff --git a/src/defines.mk b/src/defines.mk new file mode 100644 index 0000000..c07a4e1 --- /dev/null +++ b/src/defines.mk @@ -0,0 +1,7 @@ +-include /usr/share/dpkg/pkg-info.mk + +DESTDIR= +DOCDIR=${DESTDIR}/usr/share/doc/${PACKAGE} +WWWBASEDIR=${DESTDIR}/usr/share/javascript/${PACKAGE} +WWWCSSDIR=${WWWBASEDIR}/css +WWWIMAGESDIR=${WWWBASEDIR}/images diff --git a/form/BondModeSelector.js b/src/form/BondModeSelector.js similarity index 100% rename from form/BondModeSelector.js rename to src/form/BondModeSelector.js diff --git a/form/Checkbox.js b/src/form/Checkbox.js similarity index 100% rename from form/Checkbox.js rename to src/form/Checkbox.js diff --git a/form/ComboGrid.js b/src/form/ComboGrid.js similarity index 100% rename from form/ComboGrid.js rename to src/form/ComboGrid.js diff --git a/form/DateTimeField.js b/src/form/DateTimeField.js similarity index 100% rename from form/DateTimeField.js rename to src/form/DateTimeField.js diff --git a/form/DisplayEdit.js b/src/form/DisplayEdit.js similarity index 100% rename from form/DisplayEdit.js rename to src/form/DisplayEdit.js diff --git a/form/ExpireDate.js b/src/form/ExpireDate.js similarity index 100% rename from form/ExpireDate.js rename to src/form/ExpireDate.js diff --git a/form/IntegerField.js b/src/form/IntegerField.js similarity index 100% rename from form/IntegerField.js rename to src/form/IntegerField.js diff --git a/form/KVComboBox.js b/src/form/KVComboBox.js similarity index 100% rename from form/KVComboBox.js rename to src/form/KVComboBox.js diff --git a/form/LanguageSelector.js b/src/form/LanguageSelector.js similarity index 100% rename from form/LanguageSelector.js rename to src/form/LanguageSelector.js diff --git a/form/NetworkSelector.js b/src/form/NetworkSelector.js similarity index 100% rename from form/NetworkSelector.js rename to src/form/NetworkSelector.js diff --git a/form/RRDTypeSelector.js b/src/form/RRDTypeSelector.js similarity index 100% rename from form/RRDTypeSelector.js rename to src/form/RRDTypeSelector.js diff --git a/form/RealmComboBox.js b/src/form/RealmComboBox.js similarity index 100% rename from form/RealmComboBox.js rename to src/form/RealmComboBox.js diff --git a/form/RoleSelector.js b/src/form/RoleSelector.js similarity index 100% rename from form/RoleSelector.js rename to src/form/RoleSelector.js diff --git a/form/TextField.js b/src/form/TextField.js similarity index 100% rename from form/TextField.js rename to src/form/TextField.js diff --git a/grid/ObjectGrid.js b/src/grid/ObjectGrid.js similarity index 100% rename from grid/ObjectGrid.js rename to src/grid/ObjectGrid.js diff --git a/grid/PendingObjectGrid.js b/src/grid/PendingObjectGrid.js similarity index 100% rename from grid/PendingObjectGrid.js rename to src/grid/PendingObjectGrid.js diff --git a/images/Makefile b/src/images/Makefile similarity index 100% rename from images/Makefile rename to src/images/Makefile diff --git a/images/pmx-clear-trigger.png b/src/images/pmx-clear-trigger.png similarity index 100% rename from images/pmx-clear-trigger.png rename to src/images/pmx-clear-trigger.png diff --git a/mixin/CBind.js b/src/mixin/CBind.js similarity index 100% rename from mixin/CBind.js rename to src/mixin/CBind.js diff --git a/node/APT.js b/src/node/APT.js similarity index 100% rename from node/APT.js rename to src/node/APT.js diff --git a/node/DNSEdit.js b/src/node/DNSEdit.js similarity index 100% rename from node/DNSEdit.js rename to src/node/DNSEdit.js diff --git a/node/DNSView.js b/src/node/DNSView.js similarity index 100% rename from node/DNSView.js rename to src/node/DNSView.js diff --git a/node/HostsView.js b/src/node/HostsView.js similarity index 100% rename from node/HostsView.js rename to src/node/HostsView.js diff --git a/node/NetworkEdit.js b/src/node/NetworkEdit.js similarity index 100% rename from node/NetworkEdit.js rename to src/node/NetworkEdit.js diff --git a/node/NetworkView.js b/src/node/NetworkView.js similarity index 100% rename from node/NetworkView.js rename to src/node/NetworkView.js diff --git a/node/ServiceView.js b/src/node/ServiceView.js similarity index 100% rename from node/ServiceView.js rename to src/node/ServiceView.js diff --git a/node/Tasks.js b/src/node/Tasks.js similarity index 100% rename from node/Tasks.js rename to src/node/Tasks.js diff --git a/node/TimeEdit.js b/src/node/TimeEdit.js similarity index 100% rename from node/TimeEdit.js rename to src/node/TimeEdit.js diff --git a/node/TimeView.js b/src/node/TimeView.js similarity index 100% rename from node/TimeView.js rename to src/node/TimeView.js diff --git a/panel/GaugeWidget.js b/src/panel/GaugeWidget.js similarity index 100% rename from panel/GaugeWidget.js rename to src/panel/GaugeWidget.js diff --git a/panel/InputPanel.js b/src/panel/InputPanel.js similarity index 100% rename from panel/InputPanel.js rename to src/panel/InputPanel.js diff --git a/panel/JournalView.js b/src/panel/JournalView.js similarity index 100% rename from panel/JournalView.js rename to src/panel/JournalView.js diff --git a/panel/LogView.js b/src/panel/LogView.js similarity index 100% rename from panel/LogView.js rename to src/panel/LogView.js diff --git a/panel/RRDChart.js b/src/panel/RRDChart.js similarity index 100% rename from panel/RRDChart.js rename to src/panel/RRDChart.js diff --git a/window/Edit.js b/src/window/Edit.js similarity index 100% rename from window/Edit.js rename to src/window/Edit.js diff --git a/window/LanguageEdit.js b/src/window/LanguageEdit.js similarity index 100% rename from window/LanguageEdit.js rename to src/window/LanguageEdit.js diff --git a/window/PasswordEdit.js b/src/window/PasswordEdit.js similarity index 100% rename from window/PasswordEdit.js rename to src/window/PasswordEdit.js diff --git a/window/TaskViewer.js b/src/window/TaskViewer.js similarity index 100% rename from window/TaskViewer.js rename to src/window/TaskViewer.js -- 2.39.2